Summary

Exploring associations of miRNA expression levels across tumor types in TCGA with suggested correlates defined by the Immune Response Working Group:

  • Overall Leukocyte fraction
  • Individual Relative CIBERSORT fraction
  • Mutation Load
  • TCR,BCR Diversity
  • Expression of
    • IFNG,IFN-gamma
    • PRF1,Perforin
    • GZMA,Granzyme A
    • PDCD1,PD-1
    • CD274,PD-L1
    • PDCD1LG2,PD-L2
    • IL10,IL-10
    • TGFB1,TGF-beta
    • IDO1,IDO
    • HLA-A

 

Prepare data

Retrieve miRNA data from Synapse

Data are stored on Synapse in the folder syn6171109.

Load miRNA sample data

Sample characteristics are stored in a tab-delimited text file (Synapse ID: syn7222010) and can be loaded with read_tsv().

# load sample data
mirna_sample_file <- mirna_files %>% 
    filter(file.id == "syn7222010") %>% 
    .[["file_path"]]
mirna_sample_df <- read_tsv(mirna_sample_file)
Parsed with column specification:
cols(
  id = col_character(),
  Disease = col_character(),
  Sample_Type = col_integer(),
  Protocol = col_character(),
  Platform = col_character()
)

Sample filtering

Sample Quality Annotations: syn4551248 (“merged_sample_quality_annotations.tsv”)

sample_qual_file <- synGet("syn4551248", downloadLocation = "../data/tcga/")
sample_qual_df <- sample_qual_file %>% 
    getFileLocation() %>% 
    read_tsv()
Parsed with column specification:
cols(
  patient_barcode = col_character(),
  aliquot_barcode = col_character(),
  `cancer type` = col_character(),
  platform = col_character(),
  patient_annotation = col_character(),
  sample_annotation = col_character(),
  aliquot_annotation = col_character(),
  aliquot_annotation_updated = col_character(),
  AWG_excluded_because_of_pathology = col_double(),
  AWG_pathology_exclusion_reason = col_character(),
  Reviewed_by_EPC = col_double(),
  Do_not_use = col_character()
)

remove samples based on Do_not_use=True, and remove cases with AWG_excluded_because_of_pathology=True

# samples to exclude from all datasets
exclude_samples <- sample_qual_df %>% 
    mutate(AWG_excluded_because_of_pathology = parse_logical(AWG_excluded_because_of_pathology),
           Do_not_use = parse_logical(str_to_upper(Do_not_use))) %>% 
    filter(AWG_excluded_because_of_pathology | Do_not_use)

Remove samples from miRNA dataset.

mirna_sample_df <- mirna_sample_df %>% 
    filter(!(id %in% exclude_samples$aliquot_barcode))

 

Load miRNA expression data

miRNA normalized, batch corrected expression values for all samples are stored as a matrix in a CSV file (Synapse ID: syn7201053) and can be loaded with read_csv().

mirna_corr_file <- "../data/mirna_correlate_data.feather"
force_read <- FALSE
if (!file.exists(mirna_corr_file) | force_read) {
    # load normalized, batch-corrected expression data
    mirna_norm_file <- mirna_files %>% 
        filter(file.id == "syn7201053") %>% 
        .[["file_path"]]
    mirna_norm_df <- read_csv(mirna_norm_file, progress = FALSE)
    
    mirna_corr_df <- mirna_norm_df %>% 
        select(one_of(c("Genes", mirna_sample_pt_df$id)))
    
    write_feather(mirna_corr_df, mirna_corr_file)
    rm(mirna_norm_df)
} else {
    mirna_corr_df <- read_feather(mirna_corr_file)
}

 

Set up plotting colors

tcga_colors <- tribble(
    ~Color, ~Disease,
    "#ED2891", "BRCA",
    "#B2509E", "GBM",
    "#D49DC7", "LGG",
    "#C1A72F", "ACC",
    "#E8C51D", "PCPG",
    "#F9ED32", "THCA",
    "#CACCDB", "CHOL",
    "#9EDDF9", "COAD",
    "#007EB5", "ESCA",
    "#104A7F", "LIHC",
    "#6E7BA2", "PAAD",
    "#DAF1FC", "READ",
    "#00AEEF", "STAD",
    "#F6B667", "CESC",
    "#D97D25", "OV",
    "#F89420", "UCEC",
    "#FBE3C7", "UCS",
    "#009444", "HNSC",
    "#97D1A9", "UVM",
    "#754C29", "LAML",
    "#CEAC8F", "THYM",
    "#3953A4", "DLBC",
    "#BBD642", "SKCM",
    "#00A99D", "SARC",
    "#542C88", "LUAD",
    "#A084BD", "LUSC",
    "#D3C3E0", "MESO",
    "#FAD2D9", "BLCA",
    "#EA7075", "KICH",
    "#ED1C24", "KIRC",
    "#F8AFB3", "KIRP",
    "#7E1918", "PRAD",
    "#BE1E2D", "TGCT"
)
mirna_sample_df <- mirna_sample_df %>% 
    mutate(Disease = factor(Disease, levels = tcga_colors$Disease))

 

Explore miRNA data

Sample characteristics

The table mirna_sample_df contains 5 columns describing the 10543 samples in the data. The Sample_Type column corresponds to TCGA sample type codes, which are defined here. The following sample types are included in the miRNA data:

mirna_sample_df %>% 
    group_by(Sample_Type) %>% 
    tally()

 

Based on the codes, this is the distribution of sample types:

mirna_sample_df %>% 
    group_by(Sample_Type) %>% 
    tally() %>% 
    mutate(Definition = case_when(
        .$Sample_Type == 1 ~ "Primary Solid Tumor",
        .$Sample_Type == 2 ~ "Recurrent Solid Tumorr",
        .$Sample_Type == 3 ~ "Primary Blood Derived Cancer - Peripheral Blood",
        .$Sample_Type == 5 ~ "Additional - New Primary",
        .$Sample_Type == 6 ~ "Metastatic",
        .$Sample_Type == 7 ~ "Additional Metastatic",
        .$Sample_Type == 11 ~ "Solid Tissue Normal"
    ))

Note: only include “primary” tumor samples for now; also, add additional column to store vial ID (to ease mapping between data sets).

mirna_sample_pt_df <- mirna_sample_df %>% 
    filter(Sample_Type %in% c(1, 3, 5)) %>% 
    mutate(vial_id = str_replace(id, "(\\-[:alnum:]+){3}$", ""))

 

Additionally, the following disease types are included:

mirna_sample_pt_df %>% 
    group_by(Disease) %>% 
    tally()

Note: exclude LAML, THYM, and DLBC from cell content correlations

 

And technical variables:

mirna_sample_pt_df %>% 
    group_by(Protocol, Platform) %>% 
    tally()

 

miRNA expression values

Expression values in the data are reportedly reads per million (RPM). I’ve randomly selected a few samples from each disease type, sample type, protocol, and platform to inspect the distribution of expression values across all 743 miRNA genes.

set.seed(0)
# randomly select 1-3 samples from each combination of characteristics
sample_sub_df <- mirna_sample_pt_df %>% 
    group_by(Disease, Sample_Type, Protocol, Platform) %>% 
    sample_n(3, replace = TRUE) %>% 
    ungroup() %>% 
    distinct()
# subset and melt the expression data
mirna_sub_df <- mirna_corr_df %>% 
    select(one_of(c("Genes", sample_sub_df$id))) %>%
    gather("sample", "expression", -Genes)

 

Even with a shifted log (log10(x + 1)) transformation, expression values look to be more exponentially distributed than normal.

mirna_sub_df %>% 
    left_join(mirna_sample_pt_df, by = c("sample" = "id")) %>% 
    ggplot(aes(x = log10(expression + 1))) +
    stat_density(aes(group = sample), geom = "line", position = "identity", 
                 alpha = 0.2)

 

I can also look at the distribution of log-RPM values with boxplots:

mirna_sub_df %>% 
    left_join(mirna_sample_pt_df, by = c("sample" = "id")) %>% 
    ggplot(aes(x = sample, y = log10(expression + 1))) +
    geom_boxplot(aes(fill = Disease), outlier.size = 0.5) +
    theme(axis.text.x = element_blank()) +
    scale_fill_manual(values = tcga_colors$Color)

# convert expression df to matrix
mirna_corr_mat <- mirna_corr_df %>% 
    select(one_of(c("Genes", mirna_sample_pt_df$id))) %>% 
    column_to_rownames("Genes") %>% 
    as.matrix()

 

PCA

I used the prcomp() function on the transposed expression matrix (samples x genes, after transposing) to compute PCA data, which I can use to look for batch effects among samples.

mirna_corr_pca <- mirna_corr_mat %>% 
    t() %>% 
    prcomp()

(I can use the broom:tidy() function to convert data in the prcomp object into data frames for ggplot.)

pc_df <- mirna_corr_pca %>% 
    tidy("pcs")
mirna_corr_pca_df <- mirna_corr_pca %>% 
    tidy("samples") %>% 
    filter(PC <= 2) %>% 
    left_join(mirna_sample_pt_df, by = c("row" = "id"))
rm(mirna_corr_mat)

The plot below shows samples plotted as points along the first two principle components (PCs). Points are colored by disease type.

pc1_label <- pc_df %>% 
    filter(PC == 1) %>% 
    transmute(label = sprintf("PC%s [%0.2f%%]", PC, 100*percent)) %>% 
    flatten_chr()
pc2_label <- pc_df %>% 
    filter(PC == 2) %>% 
    transmute(label = sprintf("PC%s [%0.2f%%]", PC, 100*percent)) %>% 
    flatten_chr()
mirna_corr_pca_df %>% 
    spread(PC, value) %>% 
    ggplot(aes(x = `1`, y = `2`)) +
    geom_point(aes(colour = Disease)) +
    xlab(pc1_label) +
    ylab(pc2_label) +
    scale_color_manual(values = tcga_colors$Color)

 

Facetting by sample type…

mirna_corr_pca_df %>% 
    spread(PC, value) %>% 
    ggplot(aes(x = `1`, y = `2`)) +
    geom_point(aes(colour = Disease)) +
    xlab(pc1_label) +
    ylab(pc2_label) +
    scale_color_manual(values = tcga_colors$Color) +
    facet_wrap(~ Sample_Type)


 

Prepare correlate data

  • Overall Leukocyte fraction
  • Individual Relative CIBERSORT fraction
  • Mutation Load
  • TCR,BCR Diversity
  • Gene Expression

Leukocyte fraction

Retrieve/load data

Cellular Content: syn7994728 syn5808205 (“TCGA_all_leuk_estimate.masked.20170107.tsv”)

# file_data <- synGet("syn5808205", downloadLocation = "./")
leuk_frac_file <- "../data/tcga/TCGA_all_leuk_estimate.masked.20170107.tsv"
leuk_frac_df <- read_tsv(leuk_frac_file, col_names = FALSE) %>% 
    set_names(c("disease", "id", "leuk_frac"))
Parsed with column specification:
cols(
  X1 = col_character(),
  X2 = col_character(),
  X3 = col_double()
)

Sample filtering

leuk_frac_df <- leuk_frac_df %>%
    filter(!(id %in% exclude_samples$aliquot_barcode))

Sample matching

Identify matched samples between miRNA and leukocyte fraction.

leuk_frac_ids <- leuk_frac_df %>% 
    select(id) %>%
    mutate(vial_id = str_replace(id, "(\\-[:alnum:]+){3}$", "")) %>% 
    arrange()
mirna_ids <- mirna_sample_pt_df %>%
    select(id, vial_id) %>%
    arrange()
mirna_leuk_frac_shared_ids <- inner_join(mirna_ids, leuk_frac_ids, 
                                         by = "vial_id",
                                         suffix = c("_mirna", "_leuk_frac"))
# only keep samples with matched vial ID AND portion number
portion_id_minus_analyte_regex <- "([:alnum:]+\\-){4}[0-9]+"
# plate_id_only_regex <- "[:alnum:]+(?=([:alnum:]\\-[:alnum:]+){1}$)"
mirna_leuk_frac_shared_ids <- mirna_leuk_frac_shared_ids %>%
    filter((str_extract(id_mirna, portion_id_minus_analyte_regex)
            == str_extract(id_leuk_frac, portion_id_minus_analyte_regex)))

NOTE: several samples were assayed on multiple plates; average the leukocyte fraction across these before computing correlations with miRNA

Correlate data formatting

leuk_frac_corr_file <- "../data/leuk_frac_correlates_for_mirna.feather"
force_format <- FALSE
if (!file.exists(leuk_frac_corr_file) | force_format) {
    leuk_frac_corr_df <- mirna_leuk_frac_shared_ids %>% 
        left_join(leuk_frac_df, by = c("id_leuk_frac" = "id")) %>% 
        group_by(disease, vial_id) %>% 
        summarise(leuk_frac = mean(leuk_frac)) %>% 
        ungroup()
    
    write_feather(leuk_frac_corr_df, leuk_frac_corr_file)
} else {
    leuk_frac_corr_df <- read_feather(leuk_frac_corr_file)
}

Disease-wise correlations

mirna_leuk_frac_corr_file <- "../results/mirna_leuk_frac_correlation.feather"
force_compute <- FALSE
if (!file.exists(mirna_leuk_frac_corr_file) | force_compute) {
    
    # skip immune cell cancers?
    d_list <- mirna_sample_pt_df %>% 
        # filter((!Disease %in% c("LAML", "THYM", "DLBC"))) %>% 
        filter(Disease %in% leuk_frac_corr_df$disease) %>% 
        distinct(Disease) %>% 
        mutate(Disease = as.character(Disease)) %>% 
        .$Disease %>%
        set_names(.) %>% 
        as.list()
    
    corr_df_list <- mclapply(d_list, mc.cores = 4, function(d) {
        samples_d <- mirna_sample_pt_df %>%
            filter(Disease == d) %>%
            select(vial_id) %>%
            flatten_chr() %>%
            intersect(leuk_frac_corr_df$vial_id)
        source_df <- mirna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("Genes", samples_d))) %>%
            dplyr::rename(mirna = Genes) %>%
            gather(sample, x, -mirna) %>% 
            filter(!is.na(x))
        
        target_df <- leuk_frac_corr_df %>%
            filter(vial_id %in% samples_d) %>% 
            dplyr::rename(sample = vial_id, x = leuk_frac) %>% 
            mutate(correlate = "leuk_frac")
        
        corr_df <- inner_join(source_df, target_df,
                              by = "sample",
                              suffix = c("_source", "_target")) %>%
            group_by(mirna, correlate) %>%
            do(tidy(cor.test(.$x_source, .$x_target, method = "spearman"))) %>% 
            ungroup()
        corr_df[["p.adjust"]] <- p.adjust(corr_df$p.value, method = "BH")
        return(corr_df)
    })
    
    mirna_leuk_frac_corr_df <- bind_rows(corr_df_list, .id = "disease") %>% 
        mutate(correlate_type = "leukocyte fraction")
    write_feather(mirna_leuk_frac_corr_df, mirna_leuk_frac_corr_file)
} else {
    mirna_leuk_frac_corr_df <- read_feather(mirna_leuk_frac_corr_file)
}

 

CIBERSORT fraction

Retrieve/load data

Cellular Content: syn4991611 syn8024565 (“TCGA.cluster-by-CIBERSORT-relative.tsv”)

or…?

syn7337221 (“TCGA.Kallisto.fullIDs.cibersort.relative.tsv”)

# ciber_frac_file <- "../data/tcga/TCGA.cluster-by-CIBERSORT-relative.tsv"
ciber_frac_file <- "../data/tcga/TCGA.Kallisto.fullIDs.cibersort.relative.tsv"
ciber_frac_df <- read_tsv(ciber_frac_file)
Parsed with column specification:
cols(
  .default = col_double(),
  SampleID = col_character(),
  CancerType = col_character()
)
See spec(...) for full column specifications.

Sample filtering

ciber_frac_df <- ciber_frac_df %>%
    mutate(id = str_replace_all(SampleID, "\\.", "\\-")) %>% 
    filter(!(id %in% exclude_samples$aliquot_barcode))

Sample matching

Identify matched samples between miRNA and CIBERSORT fraction.

ciber_frac_ids <- ciber_frac_df %>% 
    select(id) %>%
    mutate(vial_id = str_replace(id, "(\\-[:alnum:]+){3}$", "")) %>% 
    arrange()
# only keep samples with matched vial ID AND portion number
mirna_ciber_frac_shared_ids <- inner_join(mirna_ids, ciber_frac_ids,
                                         by = "vial_id",
                                         suffix = c("_mirna", "_ciber_frac")) %>% 
    distinct() %>% 
    filter((str_extract(id_mirna, portion_id_minus_analyte_regex) 
            == str_extract(id_ciber_frac, portion_id_minus_analyte_regex)))

NOTE: several samples were assayed on multiple plates; average the CIBERSORT fraction across these before computing correlations with miRNA

Correlate data formatting

ciber_frac_corr_file <- "../data/ciber_frac_correlates_for_mirna.feather"
force_format <- TRUE
if (!file.exists(ciber_frac_corr_file) | force_format) {
    ciber_frac_corr_df <- mirna_ciber_frac_shared_ids %>%
        left_join(ciber_frac_df, by = c("id_ciber_frac" = "id")) %>% 
        select(-id_mirna, -id_ciber_frac, 
               -SampleID, -P.value, -Correlation, -RMSE) %>% 
        group_by(CancerType, vial_id) %>%
        summarise_each(funs(mean)) %>% 
        ungroup()
    
    write_feather(ciber_frac_corr_df, ciber_frac_corr_file)
} else {
    ciber_frac_corr_df <- read_feather(ciber_frac_corr_file)
}

Disease-wise correlations

mirna_ciber_frac_corr_file <- "../results/mirna_ciber_frac_correlation.feather"
force_compute <- TRUE
if (!file.exists(mirna_ciber_frac_corr_file) | force_compute) {
    
    # skip immune cell cancers?
    d_list <- mirna_sample_pt_df %>%
        # filter((!Disease %in% c("LAML", "THYM", "DLBC"))) %>% 
        filter(Disease %in% ciber_frac_corr_df$CancerType) %>% 
        distinct(Disease) %>% 
        mutate(Disease = as.character(Disease)) %>% 
        .$Disease %>%
        set_names(.) %>% 
        as.list()
    
    corr_df_list <- mclapply(d_list, mc.cores = 4, function(d) {
        samples_d <- mirna_sample_pt_df %>%
            filter(Disease == d) %>%
            select(vial_id) %>%
            flatten_chr() %>%
            intersect(ciber_frac_corr_df$vial_id)
        source_df <- mirna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("Genes", samples_d))) %>%
            dplyr::rename(mirna = Genes) %>%
            gather(sample, x, -mirna) %>% 
            filter(!is.na(x))
        target_df <- ciber_frac_corr_df %>%
            filter(vial_id %in% samples_d) %>% 
            select(-CancerType) %>% 
            gather(correlate, x, -vial_id) %>% 
            dplyr::rename(sample = vial_id)
        
        corr_df <- inner_join(source_df, target_df,
                              by = "sample",
                              suffix = c("_source", "_target")) %>%
            group_by(mirna, correlate) %>%
            do(tidy(cor.test(.$x_source, .$x_target, method = "spearman"))) %>% 
            ungroup()
        corr_df[["p.adjust"]] <- p.adjust(corr_df$p.value, method = "BH")
        return(corr_df)
    })
    
    mirna_ciber_frac_corr_df <- bind_rows(corr_df_list, .id = "disease") %>% 
        mutate(correlate_type = "CIBERSORT fraction")
    write_feather(mirna_ciber_frac_corr_df, mirna_ciber_frac_corr_file)
} else {
    mirna_ciber_frac_corr_df <- read_feather(mirna_ciber_frac_corr_file)
}

 

Mutation load

Retrieve/load data

Mutation Load: syn5706827 syn7994728 (“mutation-load”)

  • exclude LAML?
mut_load_file <- "../data/tcga/mutation-load-vial.txt"
mut_load_df <- read_tsv(mut_load_file)
Parsed with column specification:
cols(
  cohort = col_character(),
  Patient_ID = col_character(),
  Tumor_Sample_ID = col_character(),
  Tumor_Sample_ID_Vial = col_character(),
  `Silent per Mb` = col_double(),
  `Non-silent per Mb` = col_double()
)

Sample filtering

Can’t filter aliquots, as data only includes IDs specific to the level of vial

Sample matching

Identify matched samples between miRNA and mutational load.

mut_load_ids <- mut_load_df %>% 
    select(id = Tumor_Sample_ID_Vial) %>% 
    mutate(vial_id = id) %>%
    arrange()
mirna_mut_load_shared_ids <- inner_join(mirna_ids, mut_load_ids, 
                                        by = "vial_id",
                                        suffix = c("_mirna", "_mut_load"))

Correlate data formatting

mut_load_corr_file <- "../data/mut_load_correlates_for_mirna.feather"
force_format <- TRUE
if (!file.exists(mut_load_corr_file) | force_format) {
    mut_load_corr_df <- mirna_mut_load_shared_ids %>% 
        left_join(mut_load_df, by = c("id_mut_load" = "Tumor_Sample_ID_Vial"))
    
    write_feather(mut_load_corr_df, mut_load_corr_file)
} else {
    mut_load_corr_df <- read_feather(mut_load_corr_file)
}

Disease-wise correlations

mirna_mut_load_corr_file <- "../results/mirna_mut_load_correlation.feather"
force_compute <- TRUE
if (!file.exists(mirna_mut_load_corr_file) | force_compute) {
    
    # skip immune cell cancers?
    d_list <- mirna_sample_pt_df %>%
        # filter((!Disease %in% c("LAML", "THYM", "DLBC"))) %>%
        filter(Disease %in% mut_load_corr_df$cohort) %>% 
        distinct(Disease) %>% 
        mutate(Disease = as.character(Disease)) %>% 
        .$Disease %>%
        set_names(.) %>% 
        as.list()
    
    corr_df_list <- mclapply(d_list, mc.cores = 4, function(d) {
        samples_d <- mirna_sample_pt_df %>%
            filter(Disease == d) %>%
            select(vial_id) %>%
            flatten_chr() %>%
            intersect(mut_load_corr_df$vial_id)
        source_df <- mirna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("Genes", samples_d))) %>%
            dplyr::rename(mirna = Genes) %>%
            gather(sample, x, -mirna) %>% 
            filter(!is.na(x))
        target_df <- mut_load_corr_df %>%
            filter(vial_id %in% samples_d) %>% 
            select(-id_mirna, -id_mut_load, -cohort, -Patient_ID, 
                   -Tumor_Sample_ID) %>% 
            gather(correlate, x, -vial_id) %>% 
            dplyr::rename(sample = vial_id)
        
        corr_df <- inner_join(source_df, target_df,
                              by = "sample",
                              suffix = c("_source", "_target")) %>%
            group_by(mirna, correlate) %>%
            do(tidy(cor.test(.$x_source, .$x_target, method = "spearman"))) %>% 
            ungroup()
        corr_df[["p.adjust"]] <- p.adjust(corr_df$p.value, method = "BH")
        return(corr_df)
    })
    
    mirna_mut_load_corr_df <- bind_rows(corr_df_list, .id = "disease") %>% 
        mutate(correlate_type = "mutation load")
    write_feather(mirna_mut_load_corr_df, mirna_mut_load_corr_file)
} else {
    mirna_mut_load_corr_df <- read_feather(mirna_mut_load_corr_file)
}

 

TCR/BCR diversity

Retrieve/load data

T Cell Receptor / Brown et al: syn5876488 syn7063422 (“mitcr_sampleStatistics_20160714.tsv”)

tcr_div_file <- "../data/tcga/mitcr_sampleStatistics_20160714.tsv"
tcr_div_df <- read_tsv(tcr_div_file)
Parsed with column specification:
cols(
  AliquotBarcode = col_character(),
  Study = col_character(),
  SampleTypeLetterCode = col_character(),
  ParticipantBarcode = col_character(),
  SampleBarcode = col_character(),
  CGHub_analysis_id = col_character(),
  totTCR_reads = col_integer(),
  totTCRa_reads = col_integer(),
  totTCRb_reads = col_integer(),
  shannon = col_double(),
  numClones = col_integer()
)

Sample filtering

tcr_dv_df <- tcr_div_df %>%
    filter(!(AliquotBarcode %in% exclude_samples$aliquot_barcode))

Sample matching

Identify matched samples between miRNA and CIBERSORT fraction.

tcr_div_ids <- tcr_div_df %>% 
    select(id = AliquotBarcode, vial_id = SampleBarcode) %>% 
    arrange()
# only keep samples with matched vial ID AND portion number
mirna_tcr_div_shared_ids <- inner_join(mirna_ids, tcr_div_ids,
                                       by = "vial_id",
                                       suffix = c("_mirna", "_tcr_div")) %>% 
    distinct() %>% 
    filter((str_extract(id_mirna, portion_id_minus_analyte_regex) 
            == str_extract(id_tcr_div, portion_id_minus_analyte_regex)))

NOTE: several samples were assayed on multiple plates; average the TCR diversity across these before computing correlations with miRNA

Correlate data formatting

Some samples (AliquotBarcode) have 2 values for Shannon entropy, apparently corresponding to separate analyses on CGHub. I’ll go ahead and average these values per aliquot, prior to averaging Shannon values per vial ID. As a small measure of QC, I’ll also only keep observations with at least 1 TCRA read or 1 TCRB read.

tcr_div_corr_file <- "../data/tcr_div_correlates_for_mirna.feather"
force_format <- TRUE
if (!file.exists(tcr_div_corr_file) | force_format) {
    tcr_div_corr_df <- mirna_tcr_div_shared_ids %>%
        left_join(tcr_div_df, by = c("id_tcr_div" = "AliquotBarcode")) %>%
        select(-CGHub_analysis_id) %>% 
        distinct() %>% 
        filter(totTCRa_reads > 0 | totTCRb_reads > 0) %>%
        group_by(Study, vial_id, id_tcr_div) %>%
        summarize(shannon = mean(shannon)) %>%
        group_by(Study, vial_id) %>%
        summarise(shannon = mean(shannon)) %>% 
        ungroup()
    write_feather(tcr_div_corr_df, tcr_div_corr_file)
} else {
    tcr_div_corr_df <- read_feather(tcr_div_corr_file)
}

Disease-wise correlations

mirna_tcr_div_corr_file <- "../results/mirna_tcr_div_correlation.feather"
force_compute <- TRUE
if (!file.exists(mirna_tcr_div_corr_file) | force_compute) {
    
    # skip immune cell cancers
    d_list <- mirna_sample_pt_df %>%
        # filter((!Disease %in% c("LAML", "THYM", "DLBC"))) %>% 
        filter(Disease %in% tcr_div_corr_df$Study) %>% 
        distinct(Disease) %>% 
        mutate(Disease = as.character(Disease)) %>% 
        .$Disease %>%
        set_names(.) %>% 
        as.list()
    
    corr_df_list <- mclapply(d_list, mc.cores = 4, function(d) {
        samples_d <- mirna_sample_pt_df %>%
            filter(Disease == d) %>%
            select(vial_id) %>%
            flatten_chr() %>%
            intersect(tcr_div_corr_df$vial_id)
        source_df <- mirna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("Genes", samples_d))) %>%
            dplyr::rename(mirna = Genes) %>%
            gather(sample, x, -mirna) %>% 
            filter(!is.na(x))
        target_df <- tcr_div_corr_df %>%
            filter(vial_id %in% samples_d) %>% 
            select(-Study) %>% 
            dplyr::rename(sample = vial_id) %>% 
            gather(correlate, x, -sample)
        
        corr_df <- inner_join(source_df, target_df,
                              by = "sample",
                              suffix = c("_source", "_target")) %>%
            group_by(mirna, correlate) %>%
            do(tidy(cor.test(.$x_source, .$x_target, method = "spearman"))) %>% 
            ungroup()
        corr_df[["p.adjust"]] <- p.adjust(corr_df$p.value, method = "BH")
        return(corr_df)
    })
    
    mirna_tcr_div_corr_df <- bind_rows(corr_df_list, .id = "disease") %>% 
        mutate(correlate_type = "TCR diversity")
    write_feather(mirna_tcr_div_corr_df, mirna_tcr_div_corr_file)
} else {
    mirna_tcr_div_corr_df <- read_feather(mirna_tcr_div_corr_file)
}

 

Gene expression

  • Expression of
    • IFNG,IFN-gamma
    • PRF1,Perforin
    • GZMA,Granzyme A
    • PDCD1,PD-1
    • CD274,PD-L1
    • PDCD1LG2,PD-L2
    • IL10,IL-10
    • TGFB1,TGF-beta
    • IDO1,IDO
    • HLA-A

Retrieve/load mRNA data

Batch effects normalized mRNA data: syn4976363 syn4976369 (“EB++AdjustPANCAN_IlluminaHiSeq_RNASeqV2.geneExp.tsv”) syn4976366 (" EB++GeneExpAnnotation.tsv“)

Sample characteristics are stored in a tab-delimited text file (Synapse ID: syn4976366) and can be loaded with read_tsv().

# load sample data
mrna_sample_file <- mrna_files %>% 
    filter(file.id == "syn4976366") %>% 
    .[["file_path"]]
mrna_sample_df <- read_tsv(mrna_sample_file)
Parsed with column specification:
cols(
  SampleID = col_character(),
  Center = col_character(),
  platform = col_character(),
  Adjustment = col_character()
)

Sample filtering

Remove samples from mRNA dataset.

mrna_sample_df <- mrna_sample_df %>% 
    filter(!(SampleID %in% exclude_samples$aliquot_barcode))

Sample matching

Identify matched samples between miRNA and mRNA.

mrna_ids <- mrna_sample_df %>% 
    select(SampleID) %>%
    mutate(vial_id = str_replace(SampleID, "(\\-[:alnum:]+){3}$", "")) %>% 
    arrange()
mirna_mrna_shared_ids <- inner_join(mirna_ids, mrna_ids, by = "vial_id")
# only keep samples with matched vial ID AND portion number
mirna_mrna_shared_ids <- mirna_mrna_shared_ids %>%
    filter(str_extract(id, portion_id_minus_analyte_regex)
           == str_extract(SampleID, portion_id_minus_analyte_regex))

mRNA normalized, batch corrected expression values for all samples are stored as a matrix in a TSV file (Synapse ID: syn4976369) and can be loaded with read_tsv().

Correlate data formatting

List of genes accessed here and saved as a TSV at data/Cancer Immunomodulators - TCGA PanImmune Group - Direct Relationship.tsv:

gene_correlate_file <- "../data/Cancer Immunomodulators - TCGA PanImmune Group - Direct Relationship.tsv"
gene_correlate_df <- read_tsv(gene_correlate_file)
Missing column names filled in: 'X8' [8], 'X9' [9]Parsed with column specification:
cols(
  Gene = col_character(),
  `HGNC Symbol` = col_character(),
  `Entrez ID` = col_integer(),
  `Gene Family` = col_character(),
  `Immune Checkpoint` = col_character(),
  Function = col_character(),
  `Reference(s) [PMID]` = col_character(),
  X8 = col_character(),
  X9 = col_character()
)
mrna_corr_file <- "../data/mrna_correlates_for_mirna.feather"
force_format <- FALSE
if (!file.exists(mrna_corr_file) | force_format) {
    # load normalized, batch-corrected expression data
    mrna_norm_file <- mrna_files %>% 
        filter(file.id == "syn4976369") %>% 
        .[["file_path"]]
    mrna_norm_df <- read_tsv(mrna_norm_file, progress = FALSE)
    
    # gene_list <- c("IFNG", "PRF1", "GZMA", "PDCD1", "CD274", "PDCD1LG2", "IL10", 
    #                "TGFB1", "IDO1", "HLA-A")  
    mrna_corr_df <- mrna_norm_df %>% 
        separate(gene_id, c("gene_name", "gene_id"), sep = "\\|") %>% 
        filter((gene_id %in% gene_correlate_df$`Entrez ID`) 
               | (gene_name %in% gene_correlate_df$`HGNC Symbol`)) %>% 
        select(one_of(c("gene_name", "gene_id", 
                        mirna_mrna_shared_ids$SampleID)))
    write_feather(mrna_corr_df, mrna_corr_file)
    rm(mrna_norm_df)
} else {
    mrna_corr_df <- read_feather(mrna_corr_file)
}

Disease-wise correlations

mirna_mrna_corr_file <- "../results/mirna_mrna_correlation.feather"
force_compute <- TRUE
if (!file.exists(mirna_mrna_corr_file) | force_compute) {
    
    d_list <- mirna_sample_pt_df %>%
        distinct(Disease) %>% 
        mutate(Disease = as.character(Disease)) %>% 
        .$Disease %>%
        set_names(.) %>% 
        as.list()
    
    corr_df_list <- mclapply(d_list, mc.cores = 4, function(d) {
        samples_d <- mirna_sample_pt_df %>%
            filter(Disease == d) %>%
            select(vial_id) %>%
            flatten_chr() %>%
            intersect(str_replace(names(mrna_corr_df),
                                  "(\\-[:alnum:]+){3}$", ""))
        source_df <- mirna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("Genes", samples_d))) %>%
            dplyr::rename(mirna = Genes) %>%
            gather(sample, x, -mirna) %>% 
            filter(!is.na(x))
        
        target_df <- mrna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("gene_name", samples_d))) %>%
            dplyr::rename(correlate = gene_name) %>%
            gather(sample, x, -correlate) %>% 
            filter(!is.na(x))
        
        corr_df <- inner_join(source_df, target_df,
                              by = "sample",
                              suffix = c("_source", "_target")) %>%
            group_by(mirna, correlate) %>%
            do(tidy(cor.test(.$x_source, .$x_target, method = "spearman"))) %>% 
            ungroup()
        corr_df[["p.adjust"]] <- p.adjust(corr_df$p.value, method = "BH")
        return(corr_df)
    })
    
    mirna_mrna_corr_df <- bind_rows(corr_df_list, .id = "disease") %>% 
        mutate(correlate_type = "mRNA expression")
    write_feather(mirna_mrna_corr_df, mirna_mrna_corr_file)
} else {
    mirna_mrna_corr_df <- read_feather(mirna_mrna_corr_file)
}

 

Summarize results

bind_rows(mirna_leuk_frac_corr_df, mirna_ciber_frac_corr_df, 
          mirna_mut_load_corr_df, mirna_tcr_div_corr_df,
          mirna_mrna_corr_df) %>% 
    group_by(disease, correlate_type) %>% 
    summarise(num_correlations = length(estimate),
              significant = sum(p.adjust < 0.05, na.rm = TRUE),
              other = num_correlations - significant) %>% 
    gather(correlations, total, -disease, -correlate_type, -num_correlations) %>% 
    ggplot(aes(x = disease, y = total)) +
    geom_col(aes(fill = disease, alpha = correlations), colour = "slategray") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1),
          axis.text.y = element_text(angle = 45),
          legend.position = "top") +
    scale_fill_manual(values = tcga_colors$Color) +
    scale_alpha_manual(values = c(0.4, 1)) +
    guides(fill = FALSE) +
    facet_wrap(~ correlate_type, ncol = 2, scales = "free_y")

mirna_sig_corr_df <- bind_rows(mirna_leuk_frac_corr_df, mirna_ciber_frac_corr_df, 
          mirna_mut_load_corr_df, mirna_tcr_div_corr_df,
          mirna_mrna_corr_df) %>% 
    filter(p.adjust < 0.05) %>% 
    mutate(disease = factor(disease, levels = tcga_colors$Disease))
mirna_sig_corr_df %>% 
    ggplot(aes(x = disease, y = estimate)) + 
    # geom_violin(aes(fill = disease)) +
    geom_quasirandom(aes(fill = disease, colour = disease), size = 0.5,
                     alpha = 0.3, shape = 21) +
    ylab("miRNA-correlate correlation estimate (spearman)") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
    scale_fill_manual(values = tcga_colors$Color) +
    scale_colour_manual(values = tcga_colors$Color) +
    guides(fill = FALSE, colour = FALSE) +
    facet_wrap(~ correlate_type, ncol = 2, scales = "free_x")

mirna_sig_corr_df %>% 
    mutate(direction = ifelse(estimate > 0, "positive", "negative")) %>%
    group_by(disease, correlate_type, direction) %>% 
    tally() %>% 
    ungroup() %>% 
    mutate(count = ifelse(direction == "negative", -1 * n, n)) %>% 
    ggplot(aes(x = disease, y = count)) +
    geom_col(aes(fill = disease, alpha = direction), colour = "slategray") +
    # geom_violin(aes(fill = disease)) +
    # geom_quasirandom(aes(fill = disease, colour = disease), size = 0.5,
    #                  alpha = 0.3, shape = 21) +
    # ylab("miRNA-correlate correlation estimate (spearman)") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1),
          legend.position = "top") +
    scale_fill_manual(values = tcga_colors$Color) +
    scale_alpha_manual(values = c(0.4, 1)) +
    guides(fill = FALSE) +
    facet_wrap(~ correlate_type, ncol = 2, scales = "free_y")

mirna_all_corr_df <- bind_rows(mirna_leuk_frac_corr_df, mirna_ciber_frac_corr_df, 
          mirna_mut_load_corr_df, mirna_tcr_div_corr_df,
          mirna_mrna_corr_df) %>% 
    filter(!is.na(p.adjust)) %>% 
    group_by(mirna, disease, correlate_type) %>% 
    mutate(num_sig = sum(p.adjust < 0.05 & abs(estimate) > 0.5)) %>% 
    group_by(mirna, disease) %>% 
    filter(sum(num_sig > 0) > 2) %>% 
    ungroup()
mirna_all_corr_df %>% 
    group_by(disease) %>% 
    tally()
mirna_mrna_corr_df %>% 
    filter((!disease %in% c("LAML", "THYM", "DLBC"))) %>%
    filter(!is.na(p.adjust),
           p.adjust < 0.05,
           abs(estimate) > 0.7) %>%
    left_join(gene_correlate_df %>% 
                  mutate(`HGNC Symbol` =  ifelse(Gene == "TNFA",
                                                 "TNF", `HGNC Symbol`),
                         `HGNC Symbol` =  ifelse(Gene == "IL12",
                                                 "IL12A", `HGNC Symbol`),
                         `HGNC Symbol` =  ifelse(`HGNC Symbol` == "VISTA",
                                                 "C10orf54", `HGNC Symbol`)),
                  by = c("correlate" = "HGNC Symbol")) %>% 
    mutate(correlate = ifelse(is.na(`Immune Checkpoint`) & str_detect(Function, "(MHC|Granzyme)"),
                              Function, str_c("Checkpoint", `Immune Checkpoint`, "Gene")),
           correlate = ifelse(is.na(correlate), `Gene Family`, correlate)) %>% 
    select(disease, mirna, correlate, estimate, statistic, p.value, method, alternative, p.adjust, correlate_type) %>% 
    bind_rows(mirna_leuk_frac_corr_df, mirna_ciber_frac_corr_df, 
          mirna_mut_load_corr_df, mirna_tcr_div_corr_df, .) %>% 
    filter((!disease %in% c("LAML", "THYM", "DLBC"))) %>%
    filter(!is.na(p.adjust),
           p.adjust < 0.05,
           abs(estimate) > 0.7) %>%
    mutate(correlate = fct_inorder(correlate)) %>% 
    # filter(correlate_type == "mRNA expression") %>%
    # distinct(correlate) %>%
    group_by(disease, mirna, correlate, correlate_type) %>%
    summarise(estimate = mean(estimate)) %>%
    ungroup() %>% 
    mutate(direction = ifelse(estimate > 0, "positive", "negative")) %>%
    group_by(mirna, correlate, direction) %>%
    summarise(diseases = n_distinct(disease)) %>%
    group_by(mirna) %>%
    mutate(nz_diseases = sum(diseases > 0)) %>%
    ungroup() %>%
    filter(nz_diseases > 1) %>% 
    arrange(desc(nz_diseases)) %>%
    mutate(mirna = fct_inorder(mirna)) %>%
    I %>%
    ggplot(aes(y = correlate, x = mirna)) +
    geom_point(aes(fill = direction, size = diseases), shape = 21,  alpha = 0.8, colour = "black") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1))

LS0tCnRpdGxlOiAiUGFuQ2FuIElSV0cgbWlSTkEgQ29ycmVsYXRpdmUgQW5hbHlzaXMiCmF1dGhvcjogIkphbWVzIEEuIEVkZHkiCmRhdGU6ICdgciBsdWJyaWRhdGU6OnRvZGF5KClgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZmlnX3dpZHRoOiA4CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCgojIFN1bW1hcnkKCkV4cGxvcmluZyBhc3NvY2lhdGlvbnMgb2YgbWlSTkEgZXhwcmVzc2lvbiBsZXZlbHMgYWNyb3NzIHR1bW9yIHR5cGVzIGluIFRDR0Egd2l0aCBzdWdnZXN0ZWQgY29ycmVsYXRlcyBkZWZpbmVkIGJ5IHRoZSBJbW11bmUgUmVzcG9uc2UgV29ya2luZyBHcm91cDoKCisgT3ZlcmFsbCBMZXVrb2N5dGUgZnJhY3Rpb24gIAorIEluZGl2aWR1YWwgUmVsYXRpdmUgQ0lCRVJTT1JUIGZyYWN0aW9uICAKKyBNdXRhdGlvbiBMb2FkICAKKyBUQ1IsQkNSIERpdmVyc2l0eSAgCisgRXhwcmVzc2lvbiBvZiAgCiAgICArIElGTkcsSUZOLWdhbW1hICAKICAgICsgUFJGMSxQZXJmb3JpbiAgCiAgICArIEdaTUEsR3Jhbnp5bWUgQSAgCiAgICArIFBEQ0QxLFBELTEgIAogICAgKyBDRDI3NCxQRC1MMSAgCiAgICArIFBEQ0QxTEcyLFBELUwyICAKICAgICsgSUwxMCxJTC0xMCAgCiAgICArIFRHRkIxLFRHRi1iZXRhICAKICAgICsgSURPMSxJRE8gIAogICAgKyBITEEtQSAgCgoKYGBge3Igc2V0dXAsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0UsIGNhY2hlPUZBTFNFfQojIFN5bmFwc2UgY2xpZW50CmxpYnJhcnkoc3luYXBzZUNsaWVudCkKCiMgcGFyYWxsZWwgY29tcHV0aW5nCmxpYnJhcnkocGFyYWxsZWwpCgojIHZpeiBwYWNrYWdlcwpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2diZWVzd2FybSkKCiMgcGFja2FnZXMgZm9yIGdlbmVyYWwgZGF0YSBtdW5naW5nLCBmb3JtYXR0aW5nCmxpYnJhcnkoZmVhdGhlcikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGZvcmNhdHMpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkodGlkeXZlcnNlKQoKbXlfdGhlbWVfYncgPC0gZnVuY3Rpb24oKSB7CiAgICB0aGVtZV9idygpICsKICAgICAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSByZWwoMC45KSksCiAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikpCn0KZ2dwbG90Mjo6dGhlbWVfc2V0KG15X3RoZW1lX2J3KCkpCnNjYWxlX2ZpbGxfZGlzY3JldGUgPC0gZnVuY3Rpb24oLi4uKSBzY2FsZV9maWxsX2NvbG9yYmxpbmQoLi4uKQpzY2FsZV9maWxsX2NvbnRpbnVvdXMgPC0gZnVuY3Rpb24oLi4uKSBzY2FsZV9maWxsX3ZpcmlkaXMoLi4uKQpzY2FsZV9jb2xvdXJfZGlzY3JldGUgPC0gZnVuY3Rpb24oLi4uKSBzY2FsZV9jb2xvcl9jb2xvcmJsaW5kKC4uLikKc2NhbGVfY29sb3VyX2NvbnRpbnVvdXMgPC0gZnVuY3Rpb24oLi4uKSBzY2FsZV9jb2xvcl92aXJpZGlzKC4uLikKYGBgCgotLS0tLQoKXCAgCgojIFByZXBhcmUgZGF0YQoKIyMgUmV0cmlldmUgbWlSTkEgZGF0YSBmcm9tIFN5bmFwc2UKCkRhdGEgYXJlIHN0b3JlZCBvbiBTeW5hcHNlIGluIHRoZSBmb2xkZXIgYHN5bjYxNzExMDlgLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmICghZGlyLmV4aXN0cygiZGF0YSIpKSB7CiAgICBkaXIuY3JlYXRlKCJkYXRhIikKfQpzeW5hcHNlTG9naW4oKQptaXJuYV9zeW5mb2xkZXIgPC0gInN5bjYxNzExMDkiCm1pcm5hX3N5bmZpbGVzIDwtIHN5bmFwc2VRdWVyeSgKICAgIHNwcmludGYoJ3NlbGVjdCAqIGZyb20gZmlsZSB3aGVyZSBwYXJlbnRJZD09IiVzIicsIG1pcm5hX3N5bmZvbGRlcikKKQoKIyBkb3dubG9hZCBmaWxlcyBhbmQgc3RvcmUgZGF0YS9wYXRocyBpbiBuZXcgZGF0YSBmcmFtZQptaXJuYV9maWxlcyA8LSBtaXJuYV9zeW5maWxlcyAlPiUgCiAgICBtdXRhdGUoZmlsZV9kYXRhID0gbWFwKGZpbGUuaWQsIGZ1bmN0aW9uKHN5bmlkKSB7CiAgICAgICAgc3luR2V0KHN5bmlkLCBkb3dubG9hZExvY2F0aW9uID0gIi4uL2RhdGEvdGNnYS8iKQogICAgfSksCiAgICBmaWxlX3BhdGggPSBtYXBfY2hyKGZpbGVfZGF0YSwgZ2V0RmlsZUxvY2F0aW9uKSkKYGBgCgojIyBMb2FkIG1pUk5BIHNhbXBsZSBkYXRhCgpTYW1wbGUgY2hhcmFjdGVyaXN0aWNzIGFyZSBzdG9yZWQgaW4gYSB0YWItZGVsaW1pdGVkIHRleHQgZmlsZSAoU3luYXBzZSBJRDogYHN5bjcyMjIwMTBgKSBhbmQgY2FuIGJlIGxvYWRlZCB3aXRoIGByZWFkX3RzdigpYC4KCmBgYHtyfQojIGxvYWQgc2FtcGxlIGRhdGEKbWlybmFfc2FtcGxlX2ZpbGUgPC0gbWlybmFfZmlsZXMgJT4lIAogICAgZmlsdGVyKGZpbGUuaWQgPT0gInN5bjcyMjIwMTAiKSAlPiUgCiAgICAuW1siZmlsZV9wYXRoIl1dCm1pcm5hX3NhbXBsZV9kZiA8LSByZWFkX3RzdihtaXJuYV9zYW1wbGVfZmlsZSkKYGBgCgojIyBTYW1wbGUgZmlsdGVyaW5nCgpTYW1wbGUgUXVhbGl0eSBBbm5vdGF0aW9uczoKc3luNDU1MTI0OCAoIm1lcmdlZF9zYW1wbGVfcXVhbGl0eV9hbm5vdGF0aW9ucy50c3YiKQoKYGBge3J9CnNhbXBsZV9xdWFsX2ZpbGUgPC0gc3luR2V0KCJzeW40NTUxMjQ4IiwgZG93bmxvYWRMb2NhdGlvbiA9ICIuLi9kYXRhL3RjZ2EvIikKc2FtcGxlX3F1YWxfZGYgPC0gc2FtcGxlX3F1YWxfZmlsZSAlPiUgCiAgICBnZXRGaWxlTG9jYXRpb24oKSAlPiUgCiAgICByZWFkX3RzdigpCmBgYAoKcmVtb3ZlIHNhbXBsZXMgYmFzZWQgb24gYERvX25vdF91c2U9VHJ1ZWAsIGFuZCByZW1vdmUgY2FzZXMgd2l0aCBgQVdHX2V4Y2x1ZGVkX2JlY2F1c2Vfb2ZfcGF0aG9sb2d5PVRydWVgCgpgYGB7cn0KIyBzYW1wbGVzIHRvIGV4Y2x1ZGUgZnJvbSBhbGwgZGF0YXNldHMKZXhjbHVkZV9zYW1wbGVzIDwtIHNhbXBsZV9xdWFsX2RmICU+JSAKICAgIG11dGF0ZShBV0dfZXhjbHVkZWRfYmVjYXVzZV9vZl9wYXRob2xvZ3kgPSBwYXJzZV9sb2dpY2FsKEFXR19leGNsdWRlZF9iZWNhdXNlX29mX3BhdGhvbG9neSksCiAgICAgICAgICAgRG9fbm90X3VzZSA9IHBhcnNlX2xvZ2ljYWwoc3RyX3RvX3VwcGVyKERvX25vdF91c2UpKSkgJT4lIAogICAgZmlsdGVyKEFXR19leGNsdWRlZF9iZWNhdXNlX29mX3BhdGhvbG9neSB8IERvX25vdF91c2UpCmBgYAoKUmVtb3ZlIHNhbXBsZXMgZnJvbSBtaVJOQSBkYXRhc2V0LgoKYGBge3J9Cm1pcm5hX3NhbXBsZV9kZiA8LSBtaXJuYV9zYW1wbGVfZGYgJT4lIAogICAgZmlsdGVyKCEoaWQgJWluJSBleGNsdWRlX3NhbXBsZXMkYWxpcXVvdF9iYXJjb2RlKSkKYGBgCgpcICAKCiMjIExvYWQgbWlSTkEgZXhwcmVzc2lvbiBkYXRhCgptaVJOQSBub3JtYWxpemVkLCBiYXRjaCBjb3JyZWN0ZWQgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGFsbCBzYW1wbGVzIGFyZSBzdG9yZWQgYXMgYSBtYXRyaXggaW4gYSBDU1YgZmlsZSAoU3luYXBzZSBJRDogYHN5bjcyMDEwNTNgKSBhbmQgY2FuIGJlIGxvYWRlZCB3aXRoIGByZWFkX2NzdigpYC4KCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm1pcm5hX2NvcnJfZmlsZSA8LSAiLi4vZGF0YS9taXJuYV9jb3JyZWxhdGVfZGF0YS5mZWF0aGVyIgpmb3JjZV9yZWFkIDwtIEZBTFNFCmlmICghZmlsZS5leGlzdHMobWlybmFfY29ycl9maWxlKSB8IGZvcmNlX3JlYWQpIHsKICAgICMgbG9hZCBub3JtYWxpemVkLCBiYXRjaC1jb3JyZWN0ZWQgZXhwcmVzc2lvbiBkYXRhCiAgICBtaXJuYV9ub3JtX2ZpbGUgPC0gbWlybmFfZmlsZXMgJT4lIAogICAgICAgIGZpbHRlcihmaWxlLmlkID09ICJzeW43MjAxMDUzIikgJT4lIAogICAgICAgIC5bWyJmaWxlX3BhdGgiXV0KICAgIG1pcm5hX25vcm1fZGYgPC0gcmVhZF9jc3YobWlybmFfbm9ybV9maWxlLCBwcm9ncmVzcyA9IEZBTFNFKQogICAgCiAgICBtaXJuYV9jb3JyX2RmIDwtIG1pcm5hX25vcm1fZGYgJT4lIAogICAgICAgIHNlbGVjdChvbmVfb2YoYygiR2VuZXMiLCBtaXJuYV9zYW1wbGVfcHRfZGYkaWQpKSkKICAgIAogICAgd3JpdGVfZmVhdGhlcihtaXJuYV9jb3JyX2RmLCBtaXJuYV9jb3JyX2ZpbGUpCiAgICBybShtaXJuYV9ub3JtX2RmKQp9IGVsc2UgewogICAgbWlybmFfY29ycl9kZiA8LSByZWFkX2ZlYXRoZXIobWlybmFfY29ycl9maWxlKQp9CmBgYAoKXCAgCgojIyBTZXQgdXAgcGxvdHRpbmcgY29sb3JzCgpgYGB7cn0KdGNnYV9jb2xvcnMgPC0gdHJpYmJsZSgKICAgIH5Db2xvciwgfkRpc2Vhc2UsCiAgICAiI0VEMjg5MSIsICJCUkNBIiwKICAgICIjQjI1MDlFIiwgIkdCTSIsCiAgICAiI0Q0OURDNyIsICJMR0ciLAogICAgIiNDMUE3MkYiLCAiQUNDIiwKICAgICIjRThDNTFEIiwgIlBDUEciLAogICAgIiNGOUVEMzIiLCAiVEhDQSIsCiAgICAiI0NBQ0NEQiIsICJDSE9MIiwKICAgICIjOUVEREY5IiwgIkNPQUQiLAogICAgIiMwMDdFQjUiLCAiRVNDQSIsCiAgICAiIzEwNEE3RiIsICJMSUhDIiwKICAgICIjNkU3QkEyIiwgIlBBQUQiLAogICAgIiNEQUYxRkMiLCAiUkVBRCIsCiAgICAiIzAwQUVFRiIsICJTVEFEIiwKICAgICIjRjZCNjY3IiwgIkNFU0MiLAogICAgIiNEOTdEMjUiLCAiT1YiLAogICAgIiNGODk0MjAiLCAiVUNFQyIsCiAgICAiI0ZCRTNDNyIsICJVQ1MiLAogICAgIiMwMDk0NDQiLCAiSE5TQyIsCiAgICAiIzk3RDFBOSIsICJVVk0iLAogICAgIiM3NTRDMjkiLCAiTEFNTCIsCiAgICAiI0NFQUM4RiIsICJUSFlNIiwKICAgICIjMzk1M0E0IiwgIkRMQkMiLAogICAgIiNCQkQ2NDIiLCAiU0tDTSIsCiAgICAiIzAwQTk5RCIsICJTQVJDIiwKICAgICIjNTQyQzg4IiwgIkxVQUQiLAogICAgIiNBMDg0QkQiLCAiTFVTQyIsCiAgICAiI0QzQzNFMCIsICJNRVNPIiwKICAgICIjRkFEMkQ5IiwgIkJMQ0EiLAogICAgIiNFQTcwNzUiLCAiS0lDSCIsCiAgICAiI0VEMUMyNCIsICJLSVJDIiwKICAgICIjRjhBRkIzIiwgIktJUlAiLAogICAgIiM3RTE5MTgiLCAiUFJBRCIsCiAgICAiI0JFMUUyRCIsICJUR0NUIgopCgptaXJuYV9zYW1wbGVfZGYgPC0gbWlybmFfc2FtcGxlX2RmICU+JSAKICAgIG11dGF0ZShEaXNlYXNlID0gZmFjdG9yKERpc2Vhc2UsIGxldmVscyA9IHRjZ2FfY29sb3JzJERpc2Vhc2UpKQpgYGAKCi0tLS0tCgpcICAKCiMgRXhwbG9yZSBtaVJOQSBkYXRhCgojIyBTYW1wbGUgY2hhcmFjdGVyaXN0aWNzCgpUaGUgdGFibGUgYG1pcm5hX3NhbXBsZV9kZmAgY29udGFpbnMgYHIgbmNvbChtaXJuYV9zYW1wbGVfZGYpYCBjb2x1bW5zIGRlc2NyaWJpbmcgdGhlIGByIG5yb3cobWlybmFfc2FtcGxlX2RmKWAgc2FtcGxlcyBpbiB0aGUgZGF0YS4gVGhlIGBTYW1wbGVfVHlwZWAgY29sdW1uIGNvcnJlc3BvbmRzIHRvIFRDR0EgKipzYW1wbGUgdHlwZSBjb2RlcyoqLCB3aGljaCBhcmUgZGVmaW5lZCBbaGVyZV0oaHR0cHM6Ly9nZGMuY2FuY2VyLmdvdi9yZXNvdXJjZXMtdGNnYS11c2Vycy90Y2dhLWNvZGUtdGFibGVzL3NhbXBsZS10eXBlLWNvZGVzKS4gVGhlIGZvbGxvd2luZyBzYW1wbGUgdHlwZXMgYXJlIGluY2x1ZGVkIGluIHRoZSBtaVJOQSBkYXRhOgoKYGBge3J9Cm1pcm5hX3NhbXBsZV9kZiAlPiUgCiAgICBncm91cF9ieShTYW1wbGVfVHlwZSkgJT4lIAogICAgdGFsbHkoKQpgYGAKClwgIAoKQmFzZWQgb24gdGhlIGNvZGVzLCB0aGlzIGlzIHRoZSBkaXN0cmlidXRpb24gb2Ygc2FtcGxlIHR5cGVzOgoKYGBge3J9Cm1pcm5hX3NhbXBsZV9kZiAlPiUgCiAgICBncm91cF9ieShTYW1wbGVfVHlwZSkgJT4lIAogICAgdGFsbHkoKSAlPiUgCiAgICBtdXRhdGUoRGVmaW5pdGlvbiA9IGNhc2Vfd2hlbigKICAgICAgICAuJFNhbXBsZV9UeXBlID09IDEgfiAiUHJpbWFyeSBTb2xpZCBUdW1vciIsCiAgICAgICAgLiRTYW1wbGVfVHlwZSA9PSAyIH4gIlJlY3VycmVudCBTb2xpZCBUdW1vcnIiLAogICAgICAgIC4kU2FtcGxlX1R5cGUgPT0gMyB+ICJQcmltYXJ5IEJsb29kIERlcml2ZWQgQ2FuY2VyIC0gUGVyaXBoZXJhbCBCbG9vZCIsCiAgICAgICAgLiRTYW1wbGVfVHlwZSA9PSA1IH4gIkFkZGl0aW9uYWwgLSBOZXcgUHJpbWFyeSIsCiAgICAgICAgLiRTYW1wbGVfVHlwZSA9PSA2IH4gIk1ldGFzdGF0aWMiLAogICAgICAgIC4kU2FtcGxlX1R5cGUgPT0gNyB+ICJBZGRpdGlvbmFsIE1ldGFzdGF0aWMiLAogICAgICAgIC4kU2FtcGxlX1R5cGUgPT0gMTEgfiAiU29saWQgVGlzc3VlIE5vcm1hbCIKICAgICkpCmBgYAoKTm90ZTogb25seSBpbmNsdWRlICJwcmltYXJ5IiB0dW1vciBzYW1wbGVzIGZvciBub3c7IGFsc28sIGFkZCBhZGRpdGlvbmFsIGNvbHVtbiB0byBzdG9yZSB2aWFsIElEICh0byBlYXNlIG1hcHBpbmcgYmV0d2VlbiBkYXRhIHNldHMpLgoKYGBge3J9Cm1pcm5hX3NhbXBsZV9wdF9kZiA8LSBtaXJuYV9zYW1wbGVfZGYgJT4lIAogICAgZmlsdGVyKFNhbXBsZV9UeXBlICVpbiUgYygxLCAzLCA1KSkgJT4lIAogICAgbXV0YXRlKHZpYWxfaWQgPSBzdHJfcmVwbGFjZShpZCwgIihcXC1bOmFsbnVtOl0rKXszfSQiLCAiIikpCmBgYAoKXCAgCgpBZGRpdGlvbmFsbHksIHRoZSBmb2xsb3dpbmcgZGlzZWFzZSB0eXBlcyBhcmUgaW5jbHVkZWQ6CgpgYGB7cn0KbWlybmFfc2FtcGxlX3B0X2RmICU+JSAKICAgIGdyb3VwX2J5KERpc2Vhc2UpICU+JSAKICAgIHRhbGx5KCkKYGBgCgpOb3RlOiBleGNsdWRlIExBTUwsIFRIWU0sIGFuZCBETEJDIGZyb20gY2VsbCBjb250ZW50IGNvcnJlbGF0aW9ucwoKXCAgCgpBbmQgdGVjaG5pY2FsIHZhcmlhYmxlczoKCmBgYHtyfQptaXJuYV9zYW1wbGVfcHRfZGYgJT4lIAogICAgZ3JvdXBfYnkoUHJvdG9jb2wsIFBsYXRmb3JtKSAlPiUgCiAgICB0YWxseSgpCmBgYAoKXCAgCgojIyBtaVJOQSBleHByZXNzaW9uIHZhbHVlcwoKRXhwcmVzc2lvbiB2YWx1ZXMgaW4gdGhlIGRhdGEgYXJlIHJlcG9ydGVkbHkgcmVhZHMgcGVyIG1pbGxpb24gKFJQTSkuIEkndmUgcmFuZG9tbHkgc2VsZWN0ZWQgYSBmZXcgc2FtcGxlcyBmcm9tIGVhY2ggZGlzZWFzZSB0eXBlLCBzYW1wbGUgdHlwZSwgcHJvdG9jb2wsIGFuZCBwbGF0Zm9ybSB0byBpbnNwZWN0IHRoZSBkaXN0cmlidXRpb24gb2YgZXhwcmVzc2lvbiB2YWx1ZXMgYWNyb3NzIGFsbCBgciBucm93KG1pcm5hX2NvcnJfZGYpYCBtaVJOQSBnZW5lcy4KCmBgYHtyfQpzZXQuc2VlZCgwKQoKIyByYW5kb21seSBzZWxlY3QgMS0zIHNhbXBsZXMgZnJvbSBlYWNoIGNvbWJpbmF0aW9uIG9mIGNoYXJhY3RlcmlzdGljcwpzYW1wbGVfc3ViX2RmIDwtIG1pcm5hX3NhbXBsZV9wdF9kZiAlPiUgCiAgICBncm91cF9ieShEaXNlYXNlLCBTYW1wbGVfVHlwZSwgUHJvdG9jb2wsIFBsYXRmb3JtKSAlPiUgCiAgICBzYW1wbGVfbigzLCByZXBsYWNlID0gVFJVRSkgJT4lIAogICAgdW5ncm91cCgpICU+JSAKICAgIGRpc3RpbmN0KCkKCiMgc3Vic2V0IGFuZCBtZWx0IHRoZSBleHByZXNzaW9uIGRhdGEKbWlybmFfc3ViX2RmIDwtIG1pcm5hX2NvcnJfZGYgJT4lIAogICAgc2VsZWN0KG9uZV9vZihjKCJHZW5lcyIsIHNhbXBsZV9zdWJfZGYkaWQpKSkgJT4lCiAgICBnYXRoZXIoInNhbXBsZSIsICJleHByZXNzaW9uIiwgLUdlbmVzKQpgYGAKClwgIAoKRXZlbiB3aXRoIGEgc2hpZnRlZCBsb2cgKGBsb2cxMCh4ICsgMSlgKSB0cmFuc2Zvcm1hdGlvbiwgZXhwcmVzc2lvbiB2YWx1ZXMgbG9vayB0byBiZSBtb3JlIGV4cG9uZW50aWFsbHkgZGlzdHJpYnV0ZWQgdGhhbiBub3JtYWwuCgpgYGB7cn0KbWlybmFfc3ViX2RmICU+JSAKICAgIGxlZnRfam9pbihtaXJuYV9zYW1wbGVfcHRfZGYsIGJ5ID0gYygic2FtcGxlIiA9ICJpZCIpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBsb2cxMChleHByZXNzaW9uICsgMSkpKSArCiAgICBzdGF0X2RlbnNpdHkoYWVzKGdyb3VwID0gc2FtcGxlKSwgZ2VvbSA9ICJsaW5lIiwgcG9zaXRpb24gPSAiaWRlbnRpdHkiLCAKICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMikKYGBgCgpcICAKCkkgY2FuIGFsc28gbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGxvZy1SUE0gdmFsdWVzIHdpdGggYm94cGxvdHM6CgpgYGB7cn0KbWlybmFfc3ViX2RmICU+JSAKICAgIGxlZnRfam9pbihtaXJuYV9zYW1wbGVfcHRfZGYsIGJ5ID0gYygic2FtcGxlIiA9ICJpZCIpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBzYW1wbGUsIHkgPSBsb2cxMChleHByZXNzaW9uICsgMSkpKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBEaXNlYXNlKSwgb3V0bGllci5zaXplID0gMC41KSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdGNnYV9jb2xvcnMkQ29sb3IpCmBgYAoKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojIGNvbnZlcnQgZXhwcmVzc2lvbiBkZiB0byBtYXRyaXgKbWlybmFfY29ycl9tYXQgPC0gbWlybmFfY29ycl9kZiAlPiUgCiAgICBzZWxlY3Qob25lX29mKGMoIkdlbmVzIiwgbWlybmFfc2FtcGxlX3B0X2RmJGlkKSkpICU+JSAKICAgIGNvbHVtbl90b19yb3duYW1lcygiR2VuZXMiKSAlPiUgCiAgICBhcy5tYXRyaXgoKQpgYGAKXCAgCgojIyBQQ0EKCkkgdXNlZCB0aGUgYHByY29tcCgpYCBmdW5jdGlvbiBvbiB0aGUgdHJhbnNwb3NlZCBleHByZXNzaW9uIG1hdHJpeCAoc2FtcGxlcyB4IGdlbmVzLCBhZnRlciB0cmFuc3Bvc2luZykgdG8gY29tcHV0ZSBQQ0EgZGF0YSwgd2hpY2ggSSBjYW4gdXNlIHRvIGxvb2sgZm9yIGJhdGNoIGVmZmVjdHMgYW1vbmcgc2FtcGxlcy4KCmBgYHtyfQptaXJuYV9jb3JyX3BjYSA8LSBtaXJuYV9jb3JyX21hdCAlPiUgCiAgICB0KCkgJT4lIAogICAgcHJjb21wKCkKYGBgCgooSSBjYW4gdXNlIHRoZSBgYnJvb206dGlkeSgpYCBmdW5jdGlvbiB0byBjb252ZXJ0IGRhdGEgaW4gdGhlIGBwcmNvbXBgIG9iamVjdCBpbnRvIGRhdGEgZnJhbWVzIGZvciBgZ2dwbG90YC4pCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KcGNfZGYgPC0gbWlybmFfY29ycl9wY2EgJT4lIAogICAgdGlkeSgicGNzIikKCm1pcm5hX2NvcnJfcGNhX2RmIDwtIG1pcm5hX2NvcnJfcGNhICU+JSAKICAgIHRpZHkoInNhbXBsZXMiKSAlPiUgCiAgICBmaWx0ZXIoUEMgPD0gMikgJT4lIAogICAgbGVmdF9qb2luKG1pcm5hX3NhbXBsZV9wdF9kZiwgYnkgPSBjKCJyb3ciID0gImlkIikpCgpybShtaXJuYV9jb3JyX21hdCkKYGBgCgpUaGUgcGxvdCBiZWxvdyBzaG93cyBzYW1wbGVzIHBsb3R0ZWQgYXMgcG9pbnRzIGFsb25nIHRoZSBmaXJzdCB0d28gcHJpbmNpcGxlIGNvbXBvbmVudHMgKFBDcykuIFBvaW50cyBhcmUgY29sb3JlZCBieSBkaXNlYXNlIHR5cGUuCgpgYGB7cn0KcGMxX2xhYmVsIDwtIHBjX2RmICU+JSAKICAgIGZpbHRlcihQQyA9PSAxKSAlPiUgCiAgICB0cmFuc211dGUobGFiZWwgPSBzcHJpbnRmKCJQQyVzIFslMC4yZiUlXSIsIFBDLCAxMDAqcGVyY2VudCkpICU+JSAKICAgIGZsYXR0ZW5fY2hyKCkKCnBjMl9sYWJlbCA8LSBwY19kZiAlPiUgCiAgICBmaWx0ZXIoUEMgPT0gMikgJT4lIAogICAgdHJhbnNtdXRlKGxhYmVsID0gc3ByaW50ZigiUEMlcyBbJTAuMmYlJV0iLCBQQywgMTAwKnBlcmNlbnQpKSAlPiUgCiAgICBmbGF0dGVuX2NocigpCgptaXJuYV9jb3JyX3BjYV9kZiAlPiUgCiAgICBzcHJlYWQoUEMsIHZhbHVlKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBgMWAsIHkgPSBgMmApKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBEaXNlYXNlKSkgKwogICAgeGxhYihwYzFfbGFiZWwpICsKICAgIHlsYWIocGMyX2xhYmVsKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdGNnYV9jb2xvcnMkQ29sb3IpCmBgYAoKXCAgCgpGYWNldHRpbmcgYnkgc2FtcGxlIHR5cGUuLi4KCmBgYHtyfQptaXJuYV9jb3JyX3BjYV9kZiAlPiUgCiAgICBzcHJlYWQoUEMsIHZhbHVlKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBgMWAsIHkgPSBgMmApKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBEaXNlYXNlKSkgKwogICAgeGxhYihwYzFfbGFiZWwpICsKICAgIHlsYWIocGMyX2xhYmVsKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdGNnYV9jb2xvcnMkQ29sb3IpICsKICAgIGZhY2V0X3dyYXAofiBTYW1wbGVfVHlwZSkKYGBgCgotLS0tLQoKXCAgCgojIFByZXBhcmUgY29ycmVsYXRlIGRhdGEKCisgT3ZlcmFsbCBMZXVrb2N5dGUgZnJhY3Rpb24gIAorIEluZGl2aWR1YWwgUmVsYXRpdmUgQ0lCRVJTT1JUIGZyYWN0aW9uICAKKyBNdXRhdGlvbiBMb2FkICAKKyBUQ1IsQkNSIERpdmVyc2l0eSAgCisgR2VuZSBFeHByZXNzaW9uCgojIyBMZXVrb2N5dGUgZnJhY3Rpb24KCiMjIyBSZXRyaWV2ZS9sb2FkIGRhdGEKCkNlbGx1bGFyIENvbnRlbnQ6IHN5bjc5OTQ3MjgKc3luNTgwODIwNSAoIlRDR0FfYWxsX2xldWtfZXN0aW1hdGUubWFza2VkLjIwMTcwMTA3LnRzdiIpCgpgYGB7cn0KIyBmaWxlX2RhdGEgPC0gc3luR2V0KCJzeW41ODA4MjA1IiwgZG93bmxvYWRMb2NhdGlvbiA9ICIuLyIpCmxldWtfZnJhY19maWxlIDwtICIuLi9kYXRhL3RjZ2EvVENHQV9hbGxfbGV1a19lc3RpbWF0ZS5tYXNrZWQuMjAxNzAxMDcudHN2IgpsZXVrX2ZyYWNfZGYgPC0gcmVhZF90c3YobGV1a19mcmFjX2ZpbGUsIGNvbF9uYW1lcyA9IEZBTFNFKSAlPiUgCiAgICBzZXRfbmFtZXMoYygiZGlzZWFzZSIsICJpZCIsICJsZXVrX2ZyYWMiKSkKYGBgCgojIyMgU2FtcGxlIGZpbHRlcmluZwoKYGBge3J9CmxldWtfZnJhY19kZiA8LSBsZXVrX2ZyYWNfZGYgJT4lCiAgICBmaWx0ZXIoIShpZCAlaW4lIGV4Y2x1ZGVfc2FtcGxlcyRhbGlxdW90X2JhcmNvZGUpKQpgYGAKCgojIyMgU2FtcGxlIG1hdGNoaW5nCgpJZGVudGlmeSBtYXRjaGVkIHNhbXBsZXMgYmV0d2VlbiBtaVJOQSBhbmQgbGV1a29jeXRlIGZyYWN0aW9uLgoKYGBge3J9CmxldWtfZnJhY19pZHMgPC0gbGV1a19mcmFjX2RmICU+JSAKICAgIHNlbGVjdChpZCkgJT4lCiAgICBtdXRhdGUodmlhbF9pZCA9IHN0cl9yZXBsYWNlKGlkLCAiKFxcLVs6YWxudW06XSspezN9JCIsICIiKSkgJT4lIAogICAgYXJyYW5nZSgpCgptaXJuYV9pZHMgPC0gbWlybmFfc2FtcGxlX3B0X2RmICU+JQogICAgc2VsZWN0KGlkLCB2aWFsX2lkKSAlPiUKICAgIGFycmFuZ2UoKQoKbWlybmFfbGV1a19mcmFjX3NoYXJlZF9pZHMgPC0gaW5uZXJfam9pbihtaXJuYV9pZHMsIGxldWtfZnJhY19pZHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInZpYWxfaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1ZmZpeCA9IGMoIl9taXJuYSIsICJfbGV1a19mcmFjIikpCgojIG9ubHkga2VlcCBzYW1wbGVzIHdpdGggbWF0Y2hlZCB2aWFsIElEIEFORCBwb3J0aW9uIG51bWJlcgpwb3J0aW9uX2lkX21pbnVzX2FuYWx5dGVfcmVnZXggPC0gIihbOmFsbnVtOl0rXFwtKXs0fVswLTldKyIKIyBwbGF0ZV9pZF9vbmx5X3JlZ2V4IDwtICJbOmFsbnVtOl0rKD89KFs6YWxudW06XVxcLVs6YWxudW06XSspezF9JCkiCm1pcm5hX2xldWtfZnJhY19zaGFyZWRfaWRzIDwtIG1pcm5hX2xldWtfZnJhY19zaGFyZWRfaWRzICU+JQogICAgZmlsdGVyKChzdHJfZXh0cmFjdChpZF9taXJuYSwgcG9ydGlvbl9pZF9taW51c19hbmFseXRlX3JlZ2V4KQogICAgICAgICAgICA9PSBzdHJfZXh0cmFjdChpZF9sZXVrX2ZyYWMsIHBvcnRpb25faWRfbWludXNfYW5hbHl0ZV9yZWdleCkpKQpgYGAKCk5PVEU6IHNldmVyYWwgc2FtcGxlcyB3ZXJlIGFzc2F5ZWQgb24gbXVsdGlwbGUgcGxhdGVzOyBhdmVyYWdlIHRoZSBsZXVrb2N5dGUgZnJhY3Rpb24gYWNyb3NzIHRoZXNlIGJlZm9yZSBjb21wdXRpbmcgY29ycmVsYXRpb25zIHdpdGggbWlSTkEKCiMjIyBDb3JyZWxhdGUgZGF0YSBmb3JtYXR0aW5nCgpgYGB7cn0KbGV1a19mcmFjX2NvcnJfZmlsZSA8LSAiLi4vZGF0YS9sZXVrX2ZyYWNfY29ycmVsYXRlc19mb3JfbWlybmEuZmVhdGhlciIKZm9yY2VfZm9ybWF0IDwtIEZBTFNFCmlmICghZmlsZS5leGlzdHMobGV1a19mcmFjX2NvcnJfZmlsZSkgfCBmb3JjZV9mb3JtYXQpIHsKICAgIGxldWtfZnJhY19jb3JyX2RmIDwtIG1pcm5hX2xldWtfZnJhY19zaGFyZWRfaWRzICU+JSAKICAgICAgICBsZWZ0X2pvaW4obGV1a19mcmFjX2RmLCBieSA9IGMoImlkX2xldWtfZnJhYyIgPSAiaWQiKSkgJT4lIAogICAgICAgIGdyb3VwX2J5KGRpc2Vhc2UsIHZpYWxfaWQpICU+JSAKICAgICAgICBzdW1tYXJpc2UobGV1a19mcmFjID0gbWVhbihsZXVrX2ZyYWMpKSAlPiUgCiAgICAgICAgdW5ncm91cCgpCiAgICAKICAgIHdyaXRlX2ZlYXRoZXIobGV1a19mcmFjX2NvcnJfZGYsIGxldWtfZnJhY19jb3JyX2ZpbGUpCn0gZWxzZSB7CiAgICBsZXVrX2ZyYWNfY29ycl9kZiA8LSByZWFkX2ZlYXRoZXIobGV1a19mcmFjX2NvcnJfZmlsZSkKfQpgYGAKCiMjIyBEaXNlYXNlLXdpc2UgY29ycmVsYXRpb25zCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQptaXJuYV9sZXVrX2ZyYWNfY29ycl9maWxlIDwtICIuLi9yZXN1bHRzL21pcm5hX2xldWtfZnJhY19jb3JyZWxhdGlvbi5mZWF0aGVyIgpmb3JjZV9jb21wdXRlIDwtIEZBTFNFCmlmICghZmlsZS5leGlzdHMobWlybmFfbGV1a19mcmFjX2NvcnJfZmlsZSkgfCBmb3JjZV9jb21wdXRlKSB7CiAgICAKICAgICMgc2tpcCBpbW11bmUgY2VsbCBjYW5jZXJzPwogICAgZF9saXN0IDwtIG1pcm5hX3NhbXBsZV9wdF9kZiAlPiUgCiAgICAgICAgIyBmaWx0ZXIoKCFEaXNlYXNlICVpbiUgYygiTEFNTCIsICJUSFlNIiwgIkRMQkMiKSkpICU+JSAKICAgICAgICBmaWx0ZXIoRGlzZWFzZSAlaW4lIGxldWtfZnJhY19jb3JyX2RmJGRpc2Vhc2UpICU+JSAKICAgICAgICBkaXN0aW5jdChEaXNlYXNlKSAlPiUgCiAgICAgICAgbXV0YXRlKERpc2Vhc2UgPSBhcy5jaGFyYWN0ZXIoRGlzZWFzZSkpICU+JSAKICAgICAgICAuJERpc2Vhc2UgJT4lCiAgICAgICAgc2V0X25hbWVzKC4pICU+JSAKICAgICAgICBhcy5saXN0KCkKICAgIAogICAgY29ycl9kZl9saXN0IDwtIG1jbGFwcGx5KGRfbGlzdCwgbWMuY29yZXMgPSA0LCBmdW5jdGlvbihkKSB7CiAgICAgICAgc2FtcGxlc19kIDwtIG1pcm5hX3NhbXBsZV9wdF9kZiAlPiUKICAgICAgICAgICAgZmlsdGVyKERpc2Vhc2UgPT0gZCkgJT4lCiAgICAgICAgICAgIHNlbGVjdCh2aWFsX2lkKSAlPiUKICAgICAgICAgICAgZmxhdHRlbl9jaHIoKSAlPiUKICAgICAgICAgICAgaW50ZXJzZWN0KGxldWtfZnJhY19jb3JyX2RmJHZpYWxfaWQpCgogICAgICAgIHNvdXJjZV9kZiA8LSBtaXJuYV9jb3JyX2RmICU+JQogICAgICAgICAgICBzZXRfbmFtZXMoc3RyX3JlcGxhY2UobmFtZXMoLiksICIoXFwtWzphbG51bTpdKyl7M30kIiwgIiIpKSAlPiUKICAgICAgICAgICAgc2VsZWN0KG9uZV9vZihjKCJHZW5lcyIsIHNhbXBsZXNfZCkpKSAlPiUKICAgICAgICAgICAgZHBseXI6OnJlbmFtZShtaXJuYSA9IEdlbmVzKSAlPiUKICAgICAgICAgICAgZ2F0aGVyKHNhbXBsZSwgeCwgLW1pcm5hKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcighaXMubmEoeCkpCiAgICAgICAgCiAgICAgICAgdGFyZ2V0X2RmIDwtIGxldWtfZnJhY19jb3JyX2RmICU+JQogICAgICAgICAgICBmaWx0ZXIodmlhbF9pZCAlaW4lIHNhbXBsZXNfZCkgJT4lIAogICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHNhbXBsZSA9IHZpYWxfaWQsIHggPSBsZXVrX2ZyYWMpICU+JSAKICAgICAgICAgICAgbXV0YXRlKGNvcnJlbGF0ZSA9ICJsZXVrX2ZyYWMiKQogICAgICAgIAogICAgICAgIGNvcnJfZGYgPC0gaW5uZXJfam9pbihzb3VyY2VfZGYsIHRhcmdldF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAic2FtcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gYygiX3NvdXJjZSIsICJfdGFyZ2V0IikpICU+JQogICAgICAgICAgICBncm91cF9ieShtaXJuYSwgY29ycmVsYXRlKSAlPiUKICAgICAgICAgICAgZG8odGlkeShjb3IudGVzdCguJHhfc291cmNlLCAuJHhfdGFyZ2V0LCBtZXRob2QgPSAic3BlYXJtYW4iKSkpICU+JSAKICAgICAgICAgICAgdW5ncm91cCgpCiAgICAgICAgY29ycl9kZltbInAuYWRqdXN0Il1dIDwtIHAuYWRqdXN0KGNvcnJfZGYkcC52YWx1ZSwgbWV0aG9kID0gIkJIIikKICAgICAgICByZXR1cm4oY29ycl9kZikKICAgIH0pCiAgICAKICAgIG1pcm5hX2xldWtfZnJhY19jb3JyX2RmIDwtIGJpbmRfcm93cyhjb3JyX2RmX2xpc3QsIC5pZCA9ICJkaXNlYXNlIikgJT4lIAogICAgICAgIG11dGF0ZShjb3JyZWxhdGVfdHlwZSA9ICJsZXVrb2N5dGUgZnJhY3Rpb24iKQogICAgd3JpdGVfZmVhdGhlcihtaXJuYV9sZXVrX2ZyYWNfY29ycl9kZiwgbWlybmFfbGV1a19mcmFjX2NvcnJfZmlsZSkKfSBlbHNlIHsKICAgIG1pcm5hX2xldWtfZnJhY19jb3JyX2RmIDwtIHJlYWRfZmVhdGhlcihtaXJuYV9sZXVrX2ZyYWNfY29ycl9maWxlKQp9CmBgYAoKXCAgCgojIyBDSUJFUlNPUlQgZnJhY3Rpb24KCiMjIyBSZXRyaWV2ZS9sb2FkIGRhdGEKCkNlbGx1bGFyIENvbnRlbnQ6IHN5bjQ5OTE2MTEKc3luODAyNDU2NSAoIlRDR0EuY2x1c3Rlci1ieS1DSUJFUlNPUlQtcmVsYXRpdmUudHN2IikKCm9yLi4uPwoKc3luNzMzNzIyMSAoIlRDR0EuS2FsbGlzdG8uZnVsbElEcy5jaWJlcnNvcnQucmVsYXRpdmUudHN2IikKCmBgYHtyfQojIGNpYmVyX2ZyYWNfZmlsZSA8LSAiLi4vZGF0YS90Y2dhL1RDR0EuY2x1c3Rlci1ieS1DSUJFUlNPUlQtcmVsYXRpdmUudHN2IgpjaWJlcl9mcmFjX2ZpbGUgPC0gIi4uL2RhdGEvdGNnYS9UQ0dBLkthbGxpc3RvLmZ1bGxJRHMuY2liZXJzb3J0LnJlbGF0aXZlLnRzdiIKCmNpYmVyX2ZyYWNfZGYgPC0gcmVhZF90c3YoY2liZXJfZnJhY19maWxlKQpgYGAKCiMjIyBTYW1wbGUgZmlsdGVyaW5nCgpgYGB7cn0KY2liZXJfZnJhY19kZiA8LSBjaWJlcl9mcmFjX2RmICU+JQogICAgbXV0YXRlKGlkID0gc3RyX3JlcGxhY2VfYWxsKFNhbXBsZUlELCAiXFwuIiwgIlxcLSIpKSAlPiUgCiAgICBmaWx0ZXIoIShpZCAlaW4lIGV4Y2x1ZGVfc2FtcGxlcyRhbGlxdW90X2JhcmNvZGUpKQpgYGAKCiMjIyBTYW1wbGUgbWF0Y2hpbmcKCklkZW50aWZ5IG1hdGNoZWQgc2FtcGxlcyBiZXR3ZWVuIG1pUk5BIGFuZCBDSUJFUlNPUlQgZnJhY3Rpb24uCgpgYGB7cn0KY2liZXJfZnJhY19pZHMgPC0gY2liZXJfZnJhY19kZiAlPiUgCiAgICBzZWxlY3QoaWQpICU+JQogICAgbXV0YXRlKHZpYWxfaWQgPSBzdHJfcmVwbGFjZShpZCwgIihcXC1bOmFsbnVtOl0rKXszfSQiLCAiIikpICU+JSAKICAgIGFycmFuZ2UoKQoKIyBvbmx5IGtlZXAgc2FtcGxlcyB3aXRoIG1hdGNoZWQgdmlhbCBJRCBBTkQgcG9ydGlvbiBudW1iZXIKbWlybmFfY2liZXJfZnJhY19zaGFyZWRfaWRzIDwtIGlubmVyX2pvaW4obWlybmFfaWRzLCBjaWJlcl9mcmFjX2lkcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJ2aWFsX2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXggPSBjKCJfbWlybmEiLCAiX2NpYmVyX2ZyYWMiKSkgJT4lIAogICAgZGlzdGluY3QoKSAlPiUgCiAgICBmaWx0ZXIoKHN0cl9leHRyYWN0KGlkX21pcm5hLCBwb3J0aW9uX2lkX21pbnVzX2FuYWx5dGVfcmVnZXgpIAogICAgICAgICAgICA9PSBzdHJfZXh0cmFjdChpZF9jaWJlcl9mcmFjLCBwb3J0aW9uX2lkX21pbnVzX2FuYWx5dGVfcmVnZXgpKSkKYGBgCgpOT1RFOiBzZXZlcmFsIHNhbXBsZXMgd2VyZSBhc3NheWVkIG9uIG11bHRpcGxlIHBsYXRlczsgYXZlcmFnZSB0aGUgQ0lCRVJTT1JUIGZyYWN0aW9uIGFjcm9zcyB0aGVzZSBiZWZvcmUgY29tcHV0aW5nIGNvcnJlbGF0aW9ucyB3aXRoIG1pUk5BCgojIyMgQ29ycmVsYXRlIGRhdGEgZm9ybWF0dGluZwoKYGBge3J9CmNpYmVyX2ZyYWNfY29ycl9maWxlIDwtICIuLi9kYXRhL2NpYmVyX2ZyYWNfY29ycmVsYXRlc19mb3JfbWlybmEuZmVhdGhlciIKZm9yY2VfZm9ybWF0IDwtIFRSVUUKaWYgKCFmaWxlLmV4aXN0cyhjaWJlcl9mcmFjX2NvcnJfZmlsZSkgfCBmb3JjZV9mb3JtYXQpIHsKICAgIGNpYmVyX2ZyYWNfY29ycl9kZiA8LSBtaXJuYV9jaWJlcl9mcmFjX3NoYXJlZF9pZHMgJT4lCiAgICAgICAgbGVmdF9qb2luKGNpYmVyX2ZyYWNfZGYsIGJ5ID0gYygiaWRfY2liZXJfZnJhYyIgPSAiaWQiKSkgJT4lIAogICAgICAgIHNlbGVjdCgtaWRfbWlybmEsIC1pZF9jaWJlcl9mcmFjLCAKICAgICAgICAgICAgICAgLVNhbXBsZUlELCAtUC52YWx1ZSwgLUNvcnJlbGF0aW9uLCAtUk1TRSkgJT4lIAogICAgICAgIGdyb3VwX2J5KENhbmNlclR5cGUsIHZpYWxfaWQpICU+JQogICAgICAgIHN1bW1hcmlzZV9lYWNoKGZ1bnMobWVhbikpICU+JSAKICAgICAgICB1bmdyb3VwKCkKICAgIAogICAgd3JpdGVfZmVhdGhlcihjaWJlcl9mcmFjX2NvcnJfZGYsIGNpYmVyX2ZyYWNfY29ycl9maWxlKQp9IGVsc2UgewogICAgY2liZXJfZnJhY19jb3JyX2RmIDwtIHJlYWRfZmVhdGhlcihjaWJlcl9mcmFjX2NvcnJfZmlsZSkKfQpgYGAKCiMjIyBEaXNlYXNlLXdpc2UgY29ycmVsYXRpb25zCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQptaXJuYV9jaWJlcl9mcmFjX2NvcnJfZmlsZSA8LSAiLi4vcmVzdWx0cy9taXJuYV9jaWJlcl9mcmFjX2NvcnJlbGF0aW9uLmZlYXRoZXIiCmZvcmNlX2NvbXB1dGUgPC0gVFJVRQppZiAoIWZpbGUuZXhpc3RzKG1pcm5hX2NpYmVyX2ZyYWNfY29ycl9maWxlKSB8IGZvcmNlX2NvbXB1dGUpIHsKICAgIAogICAgIyBza2lwIGltbXVuZSBjZWxsIGNhbmNlcnM/CiAgICBkX2xpc3QgPC0gbWlybmFfc2FtcGxlX3B0X2RmICU+JQogICAgICAgICMgZmlsdGVyKCghRGlzZWFzZSAlaW4lIGMoIkxBTUwiLCAiVEhZTSIsICJETEJDIikpKSAlPiUgCiAgICAgICAgZmlsdGVyKERpc2Vhc2UgJWluJSBjaWJlcl9mcmFjX2NvcnJfZGYkQ2FuY2VyVHlwZSkgJT4lIAogICAgICAgIGRpc3RpbmN0KERpc2Vhc2UpICU+JSAKICAgICAgICBtdXRhdGUoRGlzZWFzZSA9IGFzLmNoYXJhY3RlcihEaXNlYXNlKSkgJT4lIAogICAgICAgIC4kRGlzZWFzZSAlPiUKICAgICAgICBzZXRfbmFtZXMoLikgJT4lIAogICAgICAgIGFzLmxpc3QoKQogICAgCiAgICBjb3JyX2RmX2xpc3QgPC0gbWNsYXBwbHkoZF9saXN0LCBtYy5jb3JlcyA9IDQsIGZ1bmN0aW9uKGQpIHsKICAgICAgICBzYW1wbGVzX2QgPC0gbWlybmFfc2FtcGxlX3B0X2RmICU+JQogICAgICAgICAgICBmaWx0ZXIoRGlzZWFzZSA9PSBkKSAlPiUKICAgICAgICAgICAgc2VsZWN0KHZpYWxfaWQpICU+JQogICAgICAgICAgICBmbGF0dGVuX2NocigpICU+JQogICAgICAgICAgICBpbnRlcnNlY3QoY2liZXJfZnJhY19jb3JyX2RmJHZpYWxfaWQpCgogICAgICAgIHNvdXJjZV9kZiA8LSBtaXJuYV9jb3JyX2RmICU+JQogICAgICAgICAgICBzZXRfbmFtZXMoc3RyX3JlcGxhY2UobmFtZXMoLiksICIoXFwtWzphbG51bTpdKyl7M30kIiwgIiIpKSAlPiUKICAgICAgICAgICAgc2VsZWN0KG9uZV9vZihjKCJHZW5lcyIsIHNhbXBsZXNfZCkpKSAlPiUKICAgICAgICAgICAgZHBseXI6OnJlbmFtZShtaXJuYSA9IEdlbmVzKSAlPiUKICAgICAgICAgICAgZ2F0aGVyKHNhbXBsZSwgeCwgLW1pcm5hKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcighaXMubmEoeCkpCgogICAgICAgIHRhcmdldF9kZiA8LSBjaWJlcl9mcmFjX2NvcnJfZGYgJT4lCiAgICAgICAgICAgIGZpbHRlcih2aWFsX2lkICVpbiUgc2FtcGxlc19kKSAlPiUgCiAgICAgICAgICAgIHNlbGVjdCgtQ2FuY2VyVHlwZSkgJT4lIAogICAgICAgICAgICBnYXRoZXIoY29ycmVsYXRlLCB4LCAtdmlhbF9pZCkgJT4lIAogICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHNhbXBsZSA9IHZpYWxfaWQpCiAgICAgICAgCiAgICAgICAgY29ycl9kZiA8LSBpbm5lcl9qb2luKHNvdXJjZV9kZiwgdGFyZ2V0X2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJzYW1wbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXggPSBjKCJfc291cmNlIiwgIl90YXJnZXQiKSkgJT4lCiAgICAgICAgICAgIGdyb3VwX2J5KG1pcm5hLCBjb3JyZWxhdGUpICU+JQogICAgICAgICAgICBkbyh0aWR5KGNvci50ZXN0KC4keF9zb3VyY2UsIC4keF90YXJnZXQsIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkgJT4lIAogICAgICAgICAgICB1bmdyb3VwKCkKICAgICAgICBjb3JyX2RmW1sicC5hZGp1c3QiXV0gPC0gcC5hZGp1c3QoY29ycl9kZiRwLnZhbHVlLCBtZXRob2QgPSAiQkgiKQogICAgICAgIHJldHVybihjb3JyX2RmKQogICAgfSkKICAgIAogICAgbWlybmFfY2liZXJfZnJhY19jb3JyX2RmIDwtIGJpbmRfcm93cyhjb3JyX2RmX2xpc3QsIC5pZCA9ICJkaXNlYXNlIikgJT4lIAogICAgICAgIG11dGF0ZShjb3JyZWxhdGVfdHlwZSA9ICJDSUJFUlNPUlQgZnJhY3Rpb24iKQogICAgd3JpdGVfZmVhdGhlcihtaXJuYV9jaWJlcl9mcmFjX2NvcnJfZGYsIG1pcm5hX2NpYmVyX2ZyYWNfY29ycl9maWxlKQp9IGVsc2UgewogICAgbWlybmFfY2liZXJfZnJhY19jb3JyX2RmIDwtIHJlYWRfZmVhdGhlcihtaXJuYV9jaWJlcl9mcmFjX2NvcnJfZmlsZSkKfQpgYGAKClwgIAoKIyMgTXV0YXRpb24gbG9hZAoKIyMjIFJldHJpZXZlL2xvYWQgZGF0YQoKTXV0YXRpb24gTG9hZDogc3luNTcwNjgyNwpzeW43OTk0NzI4ICgibXV0YXRpb24tbG9hZCIpCgoqIGV4Y2x1ZGUgTEFNTD8KCmBgYHtyfQptdXRfbG9hZF9maWxlIDwtICIuLi9kYXRhL3RjZ2EvbXV0YXRpb24tbG9hZC12aWFsLnR4dCIKbXV0X2xvYWRfZGYgPC0gcmVhZF90c3YobXV0X2xvYWRfZmlsZSkKYGBgCgojIyMgU2FtcGxlIGZpbHRlcmluZwoKQ2FuJ3QgZmlsdGVyIGFsaXF1b3RzLCBhcyBkYXRhIG9ubHkgaW5jbHVkZXMgSURzIHNwZWNpZmljIHRvIHRoZSBsZXZlbCBvZiB2aWFsCgojIyMgU2FtcGxlIG1hdGNoaW5nCgpJZGVudGlmeSBtYXRjaGVkIHNhbXBsZXMgYmV0d2VlbiBtaVJOQSBhbmQgbXV0YXRpb25hbCBsb2FkLgoKYGBge3J9Cm11dF9sb2FkX2lkcyA8LSBtdXRfbG9hZF9kZiAlPiUgCiAgICBzZWxlY3QoaWQgPSBUdW1vcl9TYW1wbGVfSURfVmlhbCkgJT4lIAogICAgbXV0YXRlKHZpYWxfaWQgPSBpZCkgJT4lCiAgICBhcnJhbmdlKCkKCm1pcm5hX211dF9sb2FkX3NoYXJlZF9pZHMgPC0gaW5uZXJfam9pbihtaXJuYV9pZHMsIG11dF9sb2FkX2lkcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJ2aWFsX2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1ZmZpeCA9IGMoIl9taXJuYSIsICJfbXV0X2xvYWQiKSkKYGBgCgojIyMgQ29ycmVsYXRlIGRhdGEgZm9ybWF0dGluZwoKYGBge3J9Cm11dF9sb2FkX2NvcnJfZmlsZSA8LSAiLi4vZGF0YS9tdXRfbG9hZF9jb3JyZWxhdGVzX2Zvcl9taXJuYS5mZWF0aGVyIgpmb3JjZV9mb3JtYXQgPC0gVFJVRQppZiAoIWZpbGUuZXhpc3RzKG11dF9sb2FkX2NvcnJfZmlsZSkgfCBmb3JjZV9mb3JtYXQpIHsKICAgIG11dF9sb2FkX2NvcnJfZGYgPC0gbWlybmFfbXV0X2xvYWRfc2hhcmVkX2lkcyAlPiUgCiAgICAgICAgbGVmdF9qb2luKG11dF9sb2FkX2RmLCBieSA9IGMoImlkX211dF9sb2FkIiA9ICJUdW1vcl9TYW1wbGVfSURfVmlhbCIpKQogICAgCiAgICB3cml0ZV9mZWF0aGVyKG11dF9sb2FkX2NvcnJfZGYsIG11dF9sb2FkX2NvcnJfZmlsZSkKfSBlbHNlIHsKICAgIG11dF9sb2FkX2NvcnJfZGYgPC0gcmVhZF9mZWF0aGVyKG11dF9sb2FkX2NvcnJfZmlsZSkKfQpgYGAKCiMjIyBEaXNlYXNlLXdpc2UgY29ycmVsYXRpb25zCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQptaXJuYV9tdXRfbG9hZF9jb3JyX2ZpbGUgPC0gIi4uL3Jlc3VsdHMvbWlybmFfbXV0X2xvYWRfY29ycmVsYXRpb24uZmVhdGhlciIKZm9yY2VfY29tcHV0ZSA8LSBUUlVFCmlmICghZmlsZS5leGlzdHMobWlybmFfbXV0X2xvYWRfY29ycl9maWxlKSB8IGZvcmNlX2NvbXB1dGUpIHsKICAgIAogICAgIyBza2lwIGltbXVuZSBjZWxsIGNhbmNlcnM/CiAgICBkX2xpc3QgPC0gbWlybmFfc2FtcGxlX3B0X2RmICU+JQogICAgICAgICMgZmlsdGVyKCghRGlzZWFzZSAlaW4lIGMoIkxBTUwiLCAiVEhZTSIsICJETEJDIikpKSAlPiUKICAgICAgICBmaWx0ZXIoRGlzZWFzZSAlaW4lIG11dF9sb2FkX2NvcnJfZGYkY29ob3J0KSAlPiUgCiAgICAgICAgZGlzdGluY3QoRGlzZWFzZSkgJT4lIAogICAgICAgIG11dGF0ZShEaXNlYXNlID0gYXMuY2hhcmFjdGVyKERpc2Vhc2UpKSAlPiUgCiAgICAgICAgLiREaXNlYXNlICU+JQogICAgICAgIHNldF9uYW1lcyguKSAlPiUgCiAgICAgICAgYXMubGlzdCgpCiAgICAKICAgIGNvcnJfZGZfbGlzdCA8LSBtY2xhcHBseShkX2xpc3QsIG1jLmNvcmVzID0gNCwgZnVuY3Rpb24oZCkgewogICAgICAgIHNhbXBsZXNfZCA8LSBtaXJuYV9zYW1wbGVfcHRfZGYgJT4lCiAgICAgICAgICAgIGZpbHRlcihEaXNlYXNlID09IGQpICU+JQogICAgICAgICAgICBzZWxlY3QodmlhbF9pZCkgJT4lCiAgICAgICAgICAgIGZsYXR0ZW5fY2hyKCkgJT4lCiAgICAgICAgICAgIGludGVyc2VjdChtdXRfbG9hZF9jb3JyX2RmJHZpYWxfaWQpCgogICAgICAgIHNvdXJjZV9kZiA8LSBtaXJuYV9jb3JyX2RmICU+JQogICAgICAgICAgICBzZXRfbmFtZXMoc3RyX3JlcGxhY2UobmFtZXMoLiksICIoXFwtWzphbG51bTpdKyl7M30kIiwgIiIpKSAlPiUKICAgICAgICAgICAgc2VsZWN0KG9uZV9vZihjKCJHZW5lcyIsIHNhbXBsZXNfZCkpKSAlPiUKICAgICAgICAgICAgZHBseXI6OnJlbmFtZShtaXJuYSA9IEdlbmVzKSAlPiUKICAgICAgICAgICAgZ2F0aGVyKHNhbXBsZSwgeCwgLW1pcm5hKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcighaXMubmEoeCkpCgogICAgICAgIHRhcmdldF9kZiA8LSBtdXRfbG9hZF9jb3JyX2RmICU+JQogICAgICAgICAgICBmaWx0ZXIodmlhbF9pZCAlaW4lIHNhbXBsZXNfZCkgJT4lIAogICAgICAgICAgICBzZWxlY3QoLWlkX21pcm5hLCAtaWRfbXV0X2xvYWQsIC1jb2hvcnQsIC1QYXRpZW50X0lELCAKICAgICAgICAgICAgICAgICAgIC1UdW1vcl9TYW1wbGVfSUQpICU+JSAKICAgICAgICAgICAgZ2F0aGVyKGNvcnJlbGF0ZSwgeCwgLXZpYWxfaWQpICU+JSAKICAgICAgICAgICAgZHBseXI6OnJlbmFtZShzYW1wbGUgPSB2aWFsX2lkKQogICAgICAgIAogICAgICAgIGNvcnJfZGYgPC0gaW5uZXJfam9pbihzb3VyY2VfZGYsIHRhcmdldF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAic2FtcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gYygiX3NvdXJjZSIsICJfdGFyZ2V0IikpICU+JQogICAgICAgICAgICBncm91cF9ieShtaXJuYSwgY29ycmVsYXRlKSAlPiUKICAgICAgICAgICAgZG8odGlkeShjb3IudGVzdCguJHhfc291cmNlLCAuJHhfdGFyZ2V0LCBtZXRob2QgPSAic3BlYXJtYW4iKSkpICU+JSAKICAgICAgICAgICAgdW5ncm91cCgpCiAgICAgICAgY29ycl9kZltbInAuYWRqdXN0Il1dIDwtIHAuYWRqdXN0KGNvcnJfZGYkcC52YWx1ZSwgbWV0aG9kID0gIkJIIikKICAgICAgICByZXR1cm4oY29ycl9kZikKICAgIH0pCiAgICAKICAgIG1pcm5hX211dF9sb2FkX2NvcnJfZGYgPC0gYmluZF9yb3dzKGNvcnJfZGZfbGlzdCwgLmlkID0gImRpc2Vhc2UiKSAlPiUgCiAgICAgICAgbXV0YXRlKGNvcnJlbGF0ZV90eXBlID0gIm11dGF0aW9uIGxvYWQiKQogICAgd3JpdGVfZmVhdGhlcihtaXJuYV9tdXRfbG9hZF9jb3JyX2RmLCBtaXJuYV9tdXRfbG9hZF9jb3JyX2ZpbGUpCn0gZWxzZSB7CiAgICBtaXJuYV9tdXRfbG9hZF9jb3JyX2RmIDwtIHJlYWRfZmVhdGhlcihtaXJuYV9tdXRfbG9hZF9jb3JyX2ZpbGUpCn0KYGBgCgpcICAKCiMjIFRDUi9CQ1IgZGl2ZXJzaXR5CgojIyMgUmV0cmlldmUvbG9hZCBkYXRhCgpUIENlbGwgUmVjZXB0b3IgLyBCcm93biBldCBhbDogc3luNTg3NjQ4OApzeW43MDYzNDIyICgibWl0Y3Jfc2FtcGxlU3RhdGlzdGljc18yMDE2MDcxNC50c3YiKQoKYGBge3J9CnRjcl9kaXZfZmlsZSA8LSAiLi4vZGF0YS90Y2dhL21pdGNyX3NhbXBsZVN0YXRpc3RpY3NfMjAxNjA3MTQudHN2IgoKdGNyX2Rpdl9kZiA8LSByZWFkX3Rzdih0Y3JfZGl2X2ZpbGUpCmBgYAoKIyMjIFNhbXBsZSBmaWx0ZXJpbmcKCmBgYHtyfQp0Y3JfZHZfZGYgPC0gdGNyX2Rpdl9kZiAlPiUKICAgIGZpbHRlcighKEFsaXF1b3RCYXJjb2RlICVpbiUgZXhjbHVkZV9zYW1wbGVzJGFsaXF1b3RfYmFyY29kZSkpCmBgYAoKIyMjIFNhbXBsZSBtYXRjaGluZwoKSWRlbnRpZnkgbWF0Y2hlZCBzYW1wbGVzIGJldHdlZW4gbWlSTkEgYW5kIENJQkVSU09SVCBmcmFjdGlvbi4KCmBgYHtyfQp0Y3JfZGl2X2lkcyA8LSB0Y3JfZGl2X2RmICU+JSAKICAgIHNlbGVjdChpZCA9IEFsaXF1b3RCYXJjb2RlLCB2aWFsX2lkID0gU2FtcGxlQmFyY29kZSkgJT4lIAogICAgYXJyYW5nZSgpCgojIG9ubHkga2VlcCBzYW1wbGVzIHdpdGggbWF0Y2hlZCB2aWFsIElEIEFORCBwb3J0aW9uIG51bWJlcgptaXJuYV90Y3JfZGl2X3NoYXJlZF9pZHMgPC0gaW5uZXJfam9pbihtaXJuYV9pZHMsIHRjcl9kaXZfaWRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJ2aWFsX2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gYygiX21pcm5hIiwgIl90Y3JfZGl2IikpICU+JSAKICAgIGRpc3RpbmN0KCkgJT4lIAogICAgZmlsdGVyKChzdHJfZXh0cmFjdChpZF9taXJuYSwgcG9ydGlvbl9pZF9taW51c19hbmFseXRlX3JlZ2V4KSAKICAgICAgICAgICAgPT0gc3RyX2V4dHJhY3QoaWRfdGNyX2RpdiwgcG9ydGlvbl9pZF9taW51c19hbmFseXRlX3JlZ2V4KSkpCmBgYAoKTk9URTogc2V2ZXJhbCBzYW1wbGVzIHdlcmUgYXNzYXllZCBvbiBtdWx0aXBsZSBwbGF0ZXM7IGF2ZXJhZ2UgdGhlIFRDUiBkaXZlcnNpdHkgYWNyb3NzIHRoZXNlIGJlZm9yZSBjb21wdXRpbmcgY29ycmVsYXRpb25zIHdpdGggbWlSTkEKCiMjIyBDb3JyZWxhdGUgZGF0YSBmb3JtYXR0aW5nCgpTb21lIHNhbXBsZXMgKGBBbGlxdW90QmFyY29kZWApIGhhdmUgMiB2YWx1ZXMgZm9yIFNoYW5ub24gZW50cm9weSwgYXBwYXJlbnRseSBjb3JyZXNwb25kaW5nIHRvIHNlcGFyYXRlIGFuYWx5c2VzIG9uIENHSHViLiBJJ2xsIGdvIGFoZWFkIGFuZCBhdmVyYWdlIHRoZXNlIHZhbHVlcyBwZXIgYWxpcXVvdCwgcHJpb3IgdG8gYXZlcmFnaW5nIFNoYW5ub24gdmFsdWVzIHBlciB2aWFsIElELiBBcyBhIHNtYWxsIG1lYXN1cmUgb2YgUUMsIEknbGwgYWxzbyBvbmx5IGtlZXAgb2JzZXJ2YXRpb25zIHdpdGggYXQgbGVhc3QgMSBUQ1JBIHJlYWQgb3IgMSBUQ1JCIHJlYWQuCgpgYGB7cn0KdGNyX2Rpdl9jb3JyX2ZpbGUgPC0gIi4uL2RhdGEvdGNyX2Rpdl9jb3JyZWxhdGVzX2Zvcl9taXJuYS5mZWF0aGVyIgpmb3JjZV9mb3JtYXQgPC0gVFJVRQppZiAoIWZpbGUuZXhpc3RzKHRjcl9kaXZfY29ycl9maWxlKSB8IGZvcmNlX2Zvcm1hdCkgewogICAgdGNyX2Rpdl9jb3JyX2RmIDwtIG1pcm5hX3Rjcl9kaXZfc2hhcmVkX2lkcyAlPiUKICAgICAgICBsZWZ0X2pvaW4odGNyX2Rpdl9kZiwgYnkgPSBjKCJpZF90Y3JfZGl2IiA9ICJBbGlxdW90QmFyY29kZSIpKSAlPiUKICAgICAgICBzZWxlY3QoLUNHSHViX2FuYWx5c2lzX2lkKSAlPiUgCiAgICAgICAgZGlzdGluY3QoKSAlPiUgCiAgICAgICAgZmlsdGVyKHRvdFRDUmFfcmVhZHMgPiAwIHwgdG90VENSYl9yZWFkcyA+IDApICU+JQogICAgICAgIGdyb3VwX2J5KFN0dWR5LCB2aWFsX2lkLCBpZF90Y3JfZGl2KSAlPiUKICAgICAgICBzdW1tYXJpemUoc2hhbm5vbiA9IG1lYW4oc2hhbm5vbikpICU+JQogICAgICAgIGdyb3VwX2J5KFN0dWR5LCB2aWFsX2lkKSAlPiUKICAgICAgICBzdW1tYXJpc2Uoc2hhbm5vbiA9IG1lYW4oc2hhbm5vbikpICU+JSAKICAgICAgICB1bmdyb3VwKCkKCiAgICB3cml0ZV9mZWF0aGVyKHRjcl9kaXZfY29ycl9kZiwgdGNyX2Rpdl9jb3JyX2ZpbGUpCn0gZWxzZSB7CiAgICB0Y3JfZGl2X2NvcnJfZGYgPC0gcmVhZF9mZWF0aGVyKHRjcl9kaXZfY29ycl9maWxlKQp9CmBgYAoKIyMjIERpc2Vhc2Utd2lzZSBjb3JyZWxhdGlvbnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm1pcm5hX3Rjcl9kaXZfY29ycl9maWxlIDwtICIuLi9yZXN1bHRzL21pcm5hX3Rjcl9kaXZfY29ycmVsYXRpb24uZmVhdGhlciIKZm9yY2VfY29tcHV0ZSA8LSBUUlVFCmlmICghZmlsZS5leGlzdHMobWlybmFfdGNyX2Rpdl9jb3JyX2ZpbGUpIHwgZm9yY2VfY29tcHV0ZSkgewogICAgCiAgICAjIHNraXAgaW1tdW5lIGNlbGwgY2FuY2VycwogICAgZF9saXN0IDwtIG1pcm5hX3NhbXBsZV9wdF9kZiAlPiUKICAgICAgICAjIGZpbHRlcigoIURpc2Vhc2UgJWluJSBjKCJMQU1MIiwgIlRIWU0iLCAiRExCQyIpKSkgJT4lIAogICAgICAgIGZpbHRlcihEaXNlYXNlICVpbiUgdGNyX2Rpdl9jb3JyX2RmJFN0dWR5KSAlPiUgCiAgICAgICAgZGlzdGluY3QoRGlzZWFzZSkgJT4lIAogICAgICAgIG11dGF0ZShEaXNlYXNlID0gYXMuY2hhcmFjdGVyKERpc2Vhc2UpKSAlPiUgCiAgICAgICAgLiREaXNlYXNlICU+JQogICAgICAgIHNldF9uYW1lcyguKSAlPiUgCiAgICAgICAgYXMubGlzdCgpCiAgICAKICAgIGNvcnJfZGZfbGlzdCA8LSBtY2xhcHBseShkX2xpc3QsIG1jLmNvcmVzID0gNCwgZnVuY3Rpb24oZCkgewogICAgICAgIHNhbXBsZXNfZCA8LSBtaXJuYV9zYW1wbGVfcHRfZGYgJT4lCiAgICAgICAgICAgIGZpbHRlcihEaXNlYXNlID09IGQpICU+JQogICAgICAgICAgICBzZWxlY3QodmlhbF9pZCkgJT4lCiAgICAgICAgICAgIGZsYXR0ZW5fY2hyKCkgJT4lCiAgICAgICAgICAgIGludGVyc2VjdCh0Y3JfZGl2X2NvcnJfZGYkdmlhbF9pZCkKCiAgICAgICAgc291cmNlX2RmIDwtIG1pcm5hX2NvcnJfZGYgJT4lCiAgICAgICAgICAgIHNldF9uYW1lcyhzdHJfcmVwbGFjZShuYW1lcyguKSwgIihcXC1bOmFsbnVtOl0rKXszfSQiLCAiIikpICU+JQogICAgICAgICAgICBzZWxlY3Qob25lX29mKGMoIkdlbmVzIiwgc2FtcGxlc19kKSkpICU+JQogICAgICAgICAgICBkcGx5cjo6cmVuYW1lKG1pcm5hID0gR2VuZXMpICU+JQogICAgICAgICAgICBnYXRoZXIoc2FtcGxlLCB4LCAtbWlybmEpICU+JSAKICAgICAgICAgICAgZmlsdGVyKCFpcy5uYSh4KSkKCiAgICAgICAgdGFyZ2V0X2RmIDwtIHRjcl9kaXZfY29ycl9kZiAlPiUKICAgICAgICAgICAgZmlsdGVyKHZpYWxfaWQgJWluJSBzYW1wbGVzX2QpICU+JSAKICAgICAgICAgICAgc2VsZWN0KC1TdHVkeSkgJT4lIAogICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHNhbXBsZSA9IHZpYWxfaWQpICU+JSAKICAgICAgICAgICAgZ2F0aGVyKGNvcnJlbGF0ZSwgeCwgLXNhbXBsZSkKICAgICAgICAKICAgICAgICBjb3JyX2RmIDwtIGlubmVyX2pvaW4oc291cmNlX2RmLCB0YXJnZXRfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInNhbXBsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1ZmZpeCA9IGMoIl9zb3VyY2UiLCAiX3RhcmdldCIpKSAlPiUKICAgICAgICAgICAgZ3JvdXBfYnkobWlybmEsIGNvcnJlbGF0ZSkgJT4lCiAgICAgICAgICAgIGRvKHRpZHkoY29yLnRlc3QoLiR4X3NvdXJjZSwgLiR4X3RhcmdldCwgbWV0aG9kID0gInNwZWFybWFuIikpKSAlPiUgCiAgICAgICAgICAgIHVuZ3JvdXAoKQogICAgICAgIGNvcnJfZGZbWyJwLmFkanVzdCJdXSA8LSBwLmFkanVzdChjb3JyX2RmJHAudmFsdWUsIG1ldGhvZCA9ICJCSCIpCiAgICAgICAgcmV0dXJuKGNvcnJfZGYpCiAgICB9KQogICAgCiAgICBtaXJuYV90Y3JfZGl2X2NvcnJfZGYgPC0gYmluZF9yb3dzKGNvcnJfZGZfbGlzdCwgLmlkID0gImRpc2Vhc2UiKSAlPiUgCiAgICAgICAgbXV0YXRlKGNvcnJlbGF0ZV90eXBlID0gIlRDUiBkaXZlcnNpdHkiKQogICAgd3JpdGVfZmVhdGhlcihtaXJuYV90Y3JfZGl2X2NvcnJfZGYsIG1pcm5hX3Rjcl9kaXZfY29ycl9maWxlKQp9IGVsc2UgewogICAgbWlybmFfdGNyX2Rpdl9jb3JyX2RmIDwtIHJlYWRfZmVhdGhlcihtaXJuYV90Y3JfZGl2X2NvcnJfZmlsZSkKfQpgYGAKClwgIAoKIyMgR2VuZSBleHByZXNzaW9uCgorIEV4cHJlc3Npb24gb2YgIAogICAgKyBJRk5HLElGTi1nYW1tYSAgCiAgICArIFBSRjEsUGVyZm9yaW4gIAogICAgKyBHWk1BLEdyYW56eW1lIEEgIAogICAgKyBQRENEMSxQRC0xICAKICAgICsgQ0QyNzQsUEQtTDEgIAogICAgKyBQRENEMUxHMixQRC1MMiAgCiAgICArIElMMTAsSUwtMTAgIAogICAgKyBUR0ZCMSxUR0YtYmV0YSAgCiAgICArIElETzEsSURPICAKICAgICsgSExBLUEgIAoKIyMjIFJldHJpZXZlL2xvYWQgbVJOQSBkYXRhCgpCYXRjaCBlZmZlY3RzIG5vcm1hbGl6ZWQgbVJOQSBkYXRhOiBzeW40OTc2MzYzCnN5bjQ5NzYzNjkgKCJFQisrQWRqdXN0UEFOQ0FOX0lsbHVtaW5hSGlTZXFfUk5BU2VxVjIuZ2VuZUV4cC50c3YiKQpzeW40OTc2MzY2ICgiIEVCKytHZW5lRXhwQW5ub3RhdGlvbi50c3YiKQoKYGBge3IgaW5jbHVkZT1GQUxTRX0KbXJuYV9zeW5mb2xkZXIgPC0gInN5bjQ5NzYzNjMiCm1ybmFfc3luZmlsZXMgPC0gc3luYXBzZVF1ZXJ5KAogICAgc3ByaW50Zignc2VsZWN0ICogZnJvbSBmaWxlIHdoZXJlIHBhcmVudElkPT0iJXMiJywgbXJuYV9zeW5mb2xkZXIpCikKCiMgZG93bmxvYWQgZmlsZXMgYW5kIHN0b3JlIGRhdGEvcGF0aHMgaW4gbmV3IGRhdGEgZnJhbWUKbXJuYV9maWxlcyA8LSBtcm5hX3N5bmZpbGVzICU+JSAKICAgIG11dGF0ZShmaWxlX2RhdGEgPSBtYXAoZmlsZS5pZCwgZnVuY3Rpb24oc3luaWQpIHsKICAgICAgICBzeW5HZXQoc3luaWQsIGRvd25sb2FkTG9jYXRpb24gPSAiLi4vZGF0YS90Y2dhIikKICAgIH0pLAogICAgZmlsZV9wYXRoID0gbWFwX2NocihmaWxlX2RhdGEsIGdldEZpbGVMb2NhdGlvbikpCmBgYAoKU2FtcGxlIGNoYXJhY3RlcmlzdGljcyBhcmUgc3RvcmVkIGluIGEgdGFiLWRlbGltaXRlZCB0ZXh0IGZpbGUgKFN5bmFwc2UgSUQ6IGBzeW40OTc2MzY2YCkgYW5kIGNhbiBiZSBsb2FkZWQgd2l0aCBgcmVhZF90c3YoKWAuCgpgYGB7cn0KIyBsb2FkIHNhbXBsZSBkYXRhCm1ybmFfc2FtcGxlX2ZpbGUgPC0gbXJuYV9maWxlcyAlPiUgCiAgICBmaWx0ZXIoZmlsZS5pZCA9PSAic3luNDk3NjM2NiIpICU+JSAKICAgIC5bWyJmaWxlX3BhdGgiXV0KbXJuYV9zYW1wbGVfZGYgPC0gcmVhZF90c3YobXJuYV9zYW1wbGVfZmlsZSkKYGBgCgojIyMgU2FtcGxlIGZpbHRlcmluZwoKUmVtb3ZlIHNhbXBsZXMgZnJvbSBtUk5BIGRhdGFzZXQuCgpgYGB7cn0KbXJuYV9zYW1wbGVfZGYgPC0gbXJuYV9zYW1wbGVfZGYgJT4lIAogICAgZmlsdGVyKCEoU2FtcGxlSUQgJWluJSBleGNsdWRlX3NhbXBsZXMkYWxpcXVvdF9iYXJjb2RlKSkKYGBgCgojIyMgU2FtcGxlIG1hdGNoaW5nCgpJZGVudGlmeSBtYXRjaGVkIHNhbXBsZXMgYmV0d2VlbiBtaVJOQSBhbmQgbVJOQS4KCmBgYHtyfQptcm5hX2lkcyA8LSBtcm5hX3NhbXBsZV9kZiAlPiUgCiAgICBzZWxlY3QoU2FtcGxlSUQpICU+JQogICAgbXV0YXRlKHZpYWxfaWQgPSBzdHJfcmVwbGFjZShTYW1wbGVJRCwgIihcXC1bOmFsbnVtOl0rKXszfSQiLCAiIikpICU+JSAKICAgIGFycmFuZ2UoKQoKbWlybmFfbXJuYV9zaGFyZWRfaWRzIDwtIGlubmVyX2pvaW4obWlybmFfaWRzLCBtcm5hX2lkcywgYnkgPSAidmlhbF9pZCIpCgojIG9ubHkga2VlcCBzYW1wbGVzIHdpdGggbWF0Y2hlZCB2aWFsIElEIEFORCBwb3J0aW9uIG51bWJlcgptaXJuYV9tcm5hX3NoYXJlZF9pZHMgPC0gbWlybmFfbXJuYV9zaGFyZWRfaWRzICU+JQogICAgZmlsdGVyKHN0cl9leHRyYWN0KGlkLCBwb3J0aW9uX2lkX21pbnVzX2FuYWx5dGVfcmVnZXgpCiAgICAgICAgICAgPT0gc3RyX2V4dHJhY3QoU2FtcGxlSUQsIHBvcnRpb25faWRfbWludXNfYW5hbHl0ZV9yZWdleCkpCmBgYAoKbVJOQSBub3JtYWxpemVkLCBiYXRjaCBjb3JyZWN0ZWQgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGFsbCBzYW1wbGVzIGFyZSBzdG9yZWQgYXMgYSBtYXRyaXggaW4gYSBUU1YgZmlsZSAoU3luYXBzZSBJRDogYHN5bjQ5NzYzNjlgKSBhbmQgY2FuIGJlIGxvYWRlZCB3aXRoIGByZWFkX3RzdigpYC4KCiMjIyBDb3JyZWxhdGUgZGF0YSBmb3JtYXR0aW5nCgpMaXN0IG9mIGdlbmVzIGFjY2Vzc2VkIFtoZXJlXShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xYXFPWFlzVTF1YmtieElaSV81cDhaUm9vdGdPQVQwS3dlTUEzTHZTWjdIWS9lZGl0I2dpZD0wKSBhbmQgc2F2ZWQgYXMgYSBUU1YgYXQgYGRhdGEvQ2FuY2VyIEltbXVub21vZHVsYXRvcnMgLSBUQ0dBIFBhbkltbXVuZSBHcm91cCAtIERpcmVjdCBSZWxhdGlvbnNoaXAudHN2YDoKCmBgYHtyfQpnZW5lX2NvcnJlbGF0ZV9maWxlIDwtICIuLi9kYXRhL0NhbmNlciBJbW11bm9tb2R1bGF0b3JzIC0gVENHQSBQYW5JbW11bmUgR3JvdXAgLSBEaXJlY3QgUmVsYXRpb25zaGlwLnRzdiIKZ2VuZV9jb3JyZWxhdGVfZGYgPC0gcmVhZF90c3YoZ2VuZV9jb3JyZWxhdGVfZmlsZSkKYGBgCgoKYGBge3J9Cm1ybmFfY29ycl9maWxlIDwtICIuLi9kYXRhL21ybmFfY29ycmVsYXRlc19mb3JfbWlybmEuZmVhdGhlciIKZm9yY2VfZm9ybWF0IDwtIEZBTFNFCmlmICghZmlsZS5leGlzdHMobXJuYV9jb3JyX2ZpbGUpIHwgZm9yY2VfZm9ybWF0KSB7CiAgICAjIGxvYWQgbm9ybWFsaXplZCwgYmF0Y2gtY29ycmVjdGVkIGV4cHJlc3Npb24gZGF0YQogICAgbXJuYV9ub3JtX2ZpbGUgPC0gbXJuYV9maWxlcyAlPiUgCiAgICAgICAgZmlsdGVyKGZpbGUuaWQgPT0gInN5bjQ5NzYzNjkiKSAlPiUgCiAgICAgICAgLltbImZpbGVfcGF0aCJdXQogICAgbXJuYV9ub3JtX2RmIDwtIHJlYWRfdHN2KG1ybmFfbm9ybV9maWxlLCBwcm9ncmVzcyA9IEZBTFNFKQogICAgCiAgICAjIGdlbmVfbGlzdCA8LSBjKCJJRk5HIiwgIlBSRjEiLCAiR1pNQSIsICJQRENEMSIsICJDRDI3NCIsICJQRENEMUxHMiIsICJJTDEwIiwgCiAgICAjICAgICAgICAgICAgICAgICJUR0ZCMSIsICJJRE8xIiwgIkhMQS1BIikgIAogICAgbXJuYV9jb3JyX2RmIDwtIG1ybmFfbm9ybV9kZiAlPiUgCiAgICAgICAgc2VwYXJhdGUoZ2VuZV9pZCwgYygiZ2VuZV9uYW1lIiwgImdlbmVfaWQiKSwgc2VwID0gIlxcfCIpICU+JSAKICAgICAgICBmaWx0ZXIoKGdlbmVfaWQgJWluJSBnZW5lX2NvcnJlbGF0ZV9kZiRgRW50cmV6IElEYCkgCiAgICAgICAgICAgICAgIHwgKGdlbmVfbmFtZSAlaW4lIGdlbmVfY29ycmVsYXRlX2RmJGBIR05DIFN5bWJvbGApKSAlPiUgCiAgICAgICAgc2VsZWN0KG9uZV9vZihjKCJnZW5lX25hbWUiLCAiZ2VuZV9pZCIsIAogICAgICAgICAgICAgICAgICAgICAgICBtaXJuYV9tcm5hX3NoYXJlZF9pZHMkU2FtcGxlSUQpKSkKICAgIHdyaXRlX2ZlYXRoZXIobXJuYV9jb3JyX2RmLCBtcm5hX2NvcnJfZmlsZSkKICAgIHJtKG1ybmFfbm9ybV9kZikKfSBlbHNlIHsKICAgIG1ybmFfY29ycl9kZiA8LSByZWFkX2ZlYXRoZXIobXJuYV9jb3JyX2ZpbGUpCn0KYGBgCgojIyMgRGlzZWFzZS13aXNlIGNvcnJlbGF0aW9ucwoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbWlybmFfbXJuYV9jb3JyX2ZpbGUgPC0gIi4uL3Jlc3VsdHMvbWlybmFfbXJuYV9jb3JyZWxhdGlvbi5mZWF0aGVyIgpmb3JjZV9jb21wdXRlIDwtIFRSVUUKaWYgKCFmaWxlLmV4aXN0cyhtaXJuYV9tcm5hX2NvcnJfZmlsZSkgfCBmb3JjZV9jb21wdXRlKSB7CiAgICAKICAgIGRfbGlzdCA8LSBtaXJuYV9zYW1wbGVfcHRfZGYgJT4lCiAgICAgICAgZGlzdGluY3QoRGlzZWFzZSkgJT4lIAogICAgICAgIG11dGF0ZShEaXNlYXNlID0gYXMuY2hhcmFjdGVyKERpc2Vhc2UpKSAlPiUgCiAgICAgICAgLiREaXNlYXNlICU+JQogICAgICAgIHNldF9uYW1lcyguKSAlPiUgCiAgICAgICAgYXMubGlzdCgpCiAgICAKICAgIGNvcnJfZGZfbGlzdCA8LSBtY2xhcHBseShkX2xpc3QsIG1jLmNvcmVzID0gNCwgZnVuY3Rpb24oZCkgewogICAgICAgIHNhbXBsZXNfZCA8LSBtaXJuYV9zYW1wbGVfcHRfZGYgJT4lCiAgICAgICAgICAgIGZpbHRlcihEaXNlYXNlID09IGQpICU+JQogICAgICAgICAgICBzZWxlY3QodmlhbF9pZCkgJT4lCiAgICAgICAgICAgIGZsYXR0ZW5fY2hyKCkgJT4lCiAgICAgICAgICAgIGludGVyc2VjdChzdHJfcmVwbGFjZShuYW1lcyhtcm5hX2NvcnJfZGYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIihcXC1bOmFsbnVtOl0rKXszfSQiLCAiIikpCgogICAgICAgIHNvdXJjZV9kZiA8LSBtaXJuYV9jb3JyX2RmICU+JQogICAgICAgICAgICBzZXRfbmFtZXMoc3RyX3JlcGxhY2UobmFtZXMoLiksICIoXFwtWzphbG51bTpdKyl7M30kIiwgIiIpKSAlPiUKICAgICAgICAgICAgc2VsZWN0KG9uZV9vZihjKCJHZW5lcyIsIHNhbXBsZXNfZCkpKSAlPiUKICAgICAgICAgICAgZHBseXI6OnJlbmFtZShtaXJuYSA9IEdlbmVzKSAlPiUKICAgICAgICAgICAgZ2F0aGVyKHNhbXBsZSwgeCwgLW1pcm5hKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcighaXMubmEoeCkpCiAgICAgICAgCiAgICAgICAgdGFyZ2V0X2RmIDwtIG1ybmFfY29ycl9kZiAlPiUKICAgICAgICAgICAgc2V0X25hbWVzKHN0cl9yZXBsYWNlKG5hbWVzKC4pLCAiKFxcLVs6YWxudW06XSspezN9JCIsICIiKSkgJT4lCiAgICAgICAgICAgIHNlbGVjdChvbmVfb2YoYygiZ2VuZV9uYW1lIiwgc2FtcGxlc19kKSkpICU+JQogICAgICAgICAgICBkcGx5cjo6cmVuYW1lKGNvcnJlbGF0ZSA9IGdlbmVfbmFtZSkgJT4lCiAgICAgICAgICAgIGdhdGhlcihzYW1wbGUsIHgsIC1jb3JyZWxhdGUpICU+JSAKICAgICAgICAgICAgZmlsdGVyKCFpcy5uYSh4KSkKICAgICAgICAKICAgICAgICBjb3JyX2RmIDwtIGlubmVyX2pvaW4oc291cmNlX2RmLCB0YXJnZXRfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInNhbXBsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1ZmZpeCA9IGMoIl9zb3VyY2UiLCAiX3RhcmdldCIpKSAlPiUKICAgICAgICAgICAgZ3JvdXBfYnkobWlybmEsIGNvcnJlbGF0ZSkgJT4lCiAgICAgICAgICAgIGRvKHRpZHkoY29yLnRlc3QoLiR4X3NvdXJjZSwgLiR4X3RhcmdldCwgbWV0aG9kID0gInNwZWFybWFuIikpKSAlPiUgCiAgICAgICAgICAgIHVuZ3JvdXAoKQogICAgICAgIGNvcnJfZGZbWyJwLmFkanVzdCJdXSA8LSBwLmFkanVzdChjb3JyX2RmJHAudmFsdWUsIG1ldGhvZCA9ICJCSCIpCiAgICAgICAgcmV0dXJuKGNvcnJfZGYpCiAgICB9KQogICAgCiAgICBtaXJuYV9tcm5hX2NvcnJfZGYgPC0gYmluZF9yb3dzKGNvcnJfZGZfbGlzdCwgLmlkID0gImRpc2Vhc2UiKSAlPiUgCiAgICAgICAgbXV0YXRlKGNvcnJlbGF0ZV90eXBlID0gIm1STkEgZXhwcmVzc2lvbiIpCiAgICB3cml0ZV9mZWF0aGVyKG1pcm5hX21ybmFfY29ycl9kZiwgbWlybmFfbXJuYV9jb3JyX2ZpbGUpCn0gZWxzZSB7CiAgICBtaXJuYV9tcm5hX2NvcnJfZGYgPC0gcmVhZF9mZWF0aGVyKG1pcm5hX21ybmFfY29ycl9maWxlKQp9CmBgYAoKLS0tLS0KClwgIAoKIyBTdW1tYXJpemUgcmVzdWx0cwoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTN9CmJpbmRfcm93cyhtaXJuYV9sZXVrX2ZyYWNfY29ycl9kZiwgbWlybmFfY2liZXJfZnJhY19jb3JyX2RmLCAKICAgICAgICAgIG1pcm5hX211dF9sb2FkX2NvcnJfZGYsIG1pcm5hX3Rjcl9kaXZfY29ycl9kZiwKICAgICAgICAgIG1pcm5hX21ybmFfY29ycl9kZikgJT4lIAogICAgZ3JvdXBfYnkoZGlzZWFzZSwgY29ycmVsYXRlX3R5cGUpICU+JSAKICAgIHN1bW1hcmlzZShudW1fY29ycmVsYXRpb25zID0gbGVuZ3RoKGVzdGltYXRlKSwKICAgICAgICAgICAgICBzaWduaWZpY2FudCA9IHN1bShwLmFkanVzdCA8IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgb3RoZXIgPSBudW1fY29ycmVsYXRpb25zIC0gc2lnbmlmaWNhbnQpICU+JSAKICAgIGdhdGhlcihjb3JyZWxhdGlvbnMsIHRvdGFsLCAtZGlzZWFzZSwgLWNvcnJlbGF0ZV90eXBlLCAtbnVtX2NvcnJlbGF0aW9ucykgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gZGlzZWFzZSwgeSA9IHRvdGFsKSkgKwogICAgZ2VvbV9jb2woYWVzKGZpbGwgPSBkaXNlYXNlLCBhbHBoYSA9IGNvcnJlbGF0aW9ucyksIGNvbG91ciA9ICJzbGF0ZWdyYXkiKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdGNnYV9jb2xvcnMkQ29sb3IpICsKICAgIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBjKDAuNCwgMSkpICsKICAgIGd1aWRlcyhmaWxsID0gRkFMU0UpICsKICAgIGZhY2V0X3dyYXAofiBjb3JyZWxhdGVfdHlwZSwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlX3kiKQpgYGAKCgpgYGB7cn0KbWlybmFfc2lnX2NvcnJfZGYgPC0gYmluZF9yb3dzKG1pcm5hX2xldWtfZnJhY19jb3JyX2RmLCBtaXJuYV9jaWJlcl9mcmFjX2NvcnJfZGYsIAogICAgICAgICAgbWlybmFfbXV0X2xvYWRfY29ycl9kZiwgbWlybmFfdGNyX2Rpdl9jb3JyX2RmLAogICAgICAgICAgbWlybmFfbXJuYV9jb3JyX2RmKSAlPiUgCiAgICBmaWx0ZXIocC5hZGp1c3QgPCAwLjA1KSAlPiUgCiAgICBtdXRhdGUoZGlzZWFzZSA9IGZhY3RvcihkaXNlYXNlLCBsZXZlbHMgPSB0Y2dhX2NvbG9ycyREaXNlYXNlKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9M30KbWlybmFfc2lnX2NvcnJfZGYgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gZGlzZWFzZSwgeSA9IGVzdGltYXRlKSkgKyAKICAgICMgZ2VvbV92aW9saW4oYWVzKGZpbGwgPSBkaXNlYXNlKSkgKwogICAgZ2VvbV9xdWFzaXJhbmRvbShhZXMoZmlsbCA9IGRpc2Vhc2UsIGNvbG91ciA9IGRpc2Vhc2UpLCBzaXplID0gMC41LAogICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMywgc2hhcGUgPSAyMSkgKwogICAgeWxhYigibWlSTkEtY29ycmVsYXRlIGNvcnJlbGF0aW9uIGVzdGltYXRlIChzcGVhcm1hbikiKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB0Y2dhX2NvbG9ycyRDb2xvcikgKwogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSB0Y2dhX2NvbG9ycyRDb2xvcikgKwogICAgZ3VpZGVzKGZpbGwgPSBGQUxTRSwgY29sb3VyID0gRkFMU0UpICsKICAgIGZhY2V0X3dyYXAofiBjb3JyZWxhdGVfdHlwZSwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlX3giKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD0zfQptaXJuYV9zaWdfY29ycl9kZiAlPiUgCiAgICBtdXRhdGUoZGlyZWN0aW9uID0gaWZlbHNlKGVzdGltYXRlID4gMCwgInBvc2l0aXZlIiwgIm5lZ2F0aXZlIikpICU+JQogICAgZ3JvdXBfYnkoZGlzZWFzZSwgY29ycmVsYXRlX3R5cGUsIGRpcmVjdGlvbikgJT4lIAogICAgdGFsbHkoKSAlPiUgCiAgICB1bmdyb3VwKCkgJT4lIAogICAgbXV0YXRlKGNvdW50ID0gaWZlbHNlKGRpcmVjdGlvbiA9PSAibmVnYXRpdmUiLCAtMSAqIG4sIG4pKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBkaXNlYXNlLCB5ID0gY291bnQpKSArCiAgICBnZW9tX2NvbChhZXMoZmlsbCA9IGRpc2Vhc2UsIGFscGhhID0gZGlyZWN0aW9uKSwgY29sb3VyID0gInNsYXRlZ3JheSIpICsKICAgICMgZ2VvbV92aW9saW4oYWVzKGZpbGwgPSBkaXNlYXNlKSkgKwogICAgIyBnZW9tX3F1YXNpcmFuZG9tKGFlcyhmaWxsID0gZGlzZWFzZSwgY29sb3VyID0gZGlzZWFzZSksIHNpemUgPSAwLjUsCiAgICAjICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjMsIHNoYXBlID0gMjEpICsKICAgICMgeWxhYigibWlSTkEtY29ycmVsYXRlIGNvcnJlbGF0aW9uIGVzdGltYXRlIChzcGVhcm1hbikiKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHRjZ2FfY29sb3JzJENvbG9yKSArCiAgICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzID0gYygwLjQsIDEpKSArCiAgICBndWlkZXMoZmlsbCA9IEZBTFNFKSArCiAgICBmYWNldF93cmFwKH4gY29ycmVsYXRlX3R5cGUsIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZV95IikKYGBgCgpgYGB7cn0KbWlybmFfYWxsX2NvcnJfZGYgPC0gYmluZF9yb3dzKG1pcm5hX2xldWtfZnJhY19jb3JyX2RmLCBtaXJuYV9jaWJlcl9mcmFjX2NvcnJfZGYsIAogICAgICAgICAgbWlybmFfbXV0X2xvYWRfY29ycl9kZiwgbWlybmFfdGNyX2Rpdl9jb3JyX2RmLAogICAgICAgICAgbWlybmFfbXJuYV9jb3JyX2RmKSAlPiUgCiAgICBmaWx0ZXIoIWlzLm5hKHAuYWRqdXN0KSkgJT4lIAogICAgZ3JvdXBfYnkobWlybmEsIGRpc2Vhc2UsIGNvcnJlbGF0ZV90eXBlKSAlPiUgCiAgICBtdXRhdGUobnVtX3NpZyA9IHN1bShwLmFkanVzdCA8IDAuMDUgJiBhYnMoZXN0aW1hdGUpID4gMC41KSkgJT4lIAogICAgZ3JvdXBfYnkobWlybmEsIGRpc2Vhc2UpICU+JSAKICAgIGZpbHRlcihzdW0obnVtX3NpZyA+IDApID4gMikgJT4lIAogICAgdW5ncm91cCgpCmBgYAoKYGBge3J9Cm1pcm5hX2FsbF9jb3JyX2RmICU+JSAKICAgIGdyb3VwX2J5KGRpc2Vhc2UpICU+JSAKICAgIHRhbGx5KCkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NH0KbWlybmFfYWxsX2NvcnJfZGYgJT4lIAogICAgZmlsdGVyKHAuYWRqdXN0IDwgMC4wNSwgYWJzKGVzdGltYXRlKSA+IDAuNSkgJT4lIAogICAgZGlzdGluY3QobWlybmEpCiAgICAjIGdyb3VwX2J5KG1pcm5hKSAlPiUgCiAgICAjIGZpbHRlcihuX2Rpc3RpbmN0KGRpc2Vhc2UpID4gMTApICU+JSAKICAgICMgdW5ncm91cCgpICU+JSAKICAgICMgIyBmaWx0ZXIoZGlzZWFzZSAlaW4lIGMoIkFDQyIsICJDRVNDIiwgIkRMQkMiLCAiRVNDQSIsICJTS0NNIikpICU+JQogICAgIyBmaWx0ZXIoY29ycmVsYXRlX3R5cGUgIT0gIm1STkEgZXhwcmVzc2lvbiIpICU+JQogICAgSQogICAgIyBnZ3Bsb3QoYWVzKHggPSBjb3JyZWxhdGUsIHkgPSBkaXNlYXNlKSkgKwogICAgIyBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBkaXNlYXNlKSkgKwogICAgIyAjIGdlb21fdGlsZShhZXMoZmlsbCA9IGVzdGltYXRlKSkgKwogICAgIyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgICAjIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gdGNnYV9jb2xvcnMkQ29sb3IpICsKICAgICMgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKSArCiAgICAjIGZhY2V0X3dyYXAofiBtaXJuYSwgbmNvbCA9IDMpCiAgICAjIGZhY2V0X2dyaWQobWlybmEgfiBjb3JyZWxhdGVfdHlwZSwgc3BhY2UgPSAiZnJlZSIsIHNjYWxlcyA9ICJmcmVlIikgJT4lIApgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02fQptaXJuYV9tcm5hX2NvcnJfZGYgJT4lIAogICAgZmlsdGVyKCghZGlzZWFzZSAlaW4lIGMoIkxBTUwiLCAiVEhZTSIsICJETEJDIikpKSAlPiUKICAgIGZpbHRlcighaXMubmEocC5hZGp1c3QpLAogICAgICAgICAgIHAuYWRqdXN0IDwgMC4wNSwKICAgICAgICAgICBhYnMoZXN0aW1hdGUpID4gMC43KSAlPiUKICAgIGxlZnRfam9pbihnZW5lX2NvcnJlbGF0ZV9kZiAlPiUgCiAgICAgICAgICAgICAgICAgIG11dGF0ZShgSEdOQyBTeW1ib2xgID0gIGlmZWxzZShHZW5lID09ICJUTkZBIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUTkYiLCBgSEdOQyBTeW1ib2xgKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGBIR05DIFN5bWJvbGAgPSAgaWZlbHNlKEdlbmUgPT0gIklMMTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIklMMTJBIiwgYEhHTkMgU3ltYm9sYCksCiAgICAgICAgICAgICAgICAgICAgICAgICBgSEdOQyBTeW1ib2xgID0gIGlmZWxzZShgSEdOQyBTeW1ib2xgID09ICJWSVNUQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzEwb3JmNTQiLCBgSEdOQyBTeW1ib2xgKSksCiAgICAgICAgICAgICAgICAgIGJ5ID0gYygiY29ycmVsYXRlIiA9ICJIR05DIFN5bWJvbCIpKSAlPiUgCiAgICBtdXRhdGUoY29ycmVsYXRlID0gaWZlbHNlKGlzLm5hKGBJbW11bmUgQ2hlY2twb2ludGApICYgc3RyX2RldGVjdChGdW5jdGlvbiwgIihNSEN8R3Jhbnp5bWUpIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZ1bmN0aW9uLCBzdHJfYygiQ2hlY2twb2ludCIsIGBJbW11bmUgQ2hlY2twb2ludGAsICJHZW5lIikpLAogICAgICAgICAgIGNvcnJlbGF0ZSA9IGlmZWxzZShpcy5uYShjb3JyZWxhdGUpLCBgR2VuZSBGYW1pbHlgLCBjb3JyZWxhdGUpKSAlPiUgCiAgICBzZWxlY3QoZGlzZWFzZSwgbWlybmEsIGNvcnJlbGF0ZSwgZXN0aW1hdGUsIHN0YXRpc3RpYywgcC52YWx1ZSwgbWV0aG9kLCBhbHRlcm5hdGl2ZSwgcC5hZGp1c3QsIGNvcnJlbGF0ZV90eXBlKSAlPiUgCiAgICBiaW5kX3Jvd3MobWlybmFfbGV1a19mcmFjX2NvcnJfZGYsIG1pcm5hX2NpYmVyX2ZyYWNfY29ycl9kZiwgCiAgICAgICAgICBtaXJuYV9tdXRfbG9hZF9jb3JyX2RmLCBtaXJuYV90Y3JfZGl2X2NvcnJfZGYsIC4pICU+JSAKICAgIGZpbHRlcigoIWRpc2Vhc2UgJWluJSBjKCJMQU1MIiwgIlRIWU0iLCAiRExCQyIpKSkgJT4lCiAgICBmaWx0ZXIoIWlzLm5hKHAuYWRqdXN0KSwKICAgICAgICAgICBwLmFkanVzdCA8IDAuMDUsCiAgICAgICAgICAgYWJzKGVzdGltYXRlKSA+IDAuNykgJT4lCiAgICBtdXRhdGUoY29ycmVsYXRlID0gZmN0X2lub3JkZXIoY29ycmVsYXRlKSkgJT4lIAogICAgIyBmaWx0ZXIoY29ycmVsYXRlX3R5cGUgPT0gIm1STkEgZXhwcmVzc2lvbiIpICU+JQogICAgIyBkaXN0aW5jdChjb3JyZWxhdGUpICU+JQogICAgZ3JvdXBfYnkoZGlzZWFzZSwgbWlybmEsIGNvcnJlbGF0ZSwgY29ycmVsYXRlX3R5cGUpICU+JQogICAgc3VtbWFyaXNlKGVzdGltYXRlID0gbWVhbihlc3RpbWF0ZSkpICU+JQogICAgdW5ncm91cCgpICU+JSAKICAgIG11dGF0ZShkaXJlY3Rpb24gPSBpZmVsc2UoZXN0aW1hdGUgPiAwLCAicG9zaXRpdmUiLCAibmVnYXRpdmUiKSkgJT4lCiAgICBncm91cF9ieShtaXJuYSwgY29ycmVsYXRlLCBkaXJlY3Rpb24pICU+JQogICAgc3VtbWFyaXNlKGRpc2Vhc2VzID0gbl9kaXN0aW5jdChkaXNlYXNlKSkgJT4lCiAgICBncm91cF9ieShtaXJuYSkgJT4lCiAgICBtdXRhdGUobnpfZGlzZWFzZXMgPSBzdW0oZGlzZWFzZXMgPiAwKSkgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBmaWx0ZXIobnpfZGlzZWFzZXMgPiAxKSAlPiUgCiAgICBhcnJhbmdlKGRlc2MobnpfZGlzZWFzZXMpKSAlPiUKICAgIG11dGF0ZShtaXJuYSA9IGZjdF9pbm9yZGVyKG1pcm5hKSkgJT4lCiAgICBJICU+JQogICAgZ2dwbG90KGFlcyh5ID0gY29ycmVsYXRlLCB4ID0gbWlybmEpKSArCiAgICBnZW9tX3BvaW50KGFlcyhmaWxsID0gZGlyZWN0aW9uLCBzaXplID0gZGlzZWFzZXMpLCBzaGFwZSA9IDIxLCAgYWxwaGEgPSAwLjgsIGNvbG91ciA9ICJibGFjayIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCmBgYAoK